JavaScript 中内存泄漏的几种情况

127 阅读2分钟

一、内存泄漏定义

内存泄漏是指程序未能释放不再使用的内存,导致内存浪费。在持续运行的服务进程中,不及时释放内存可能导致系统性能下降或进程崩溃。

二、垃圾回收机制

JavaScript 具有自动垃圾回收机制(GC),负责管理代码执行过程中使用的内存。垃圾回收机制有两种实现方式:标记清除和引用计数。

标记清除

JavaScript 最常用的垃圾回收机制。变量进入执行环境时被标记为“进入环境”,离开环境时标记为“离开环境”。垃圾回收程序运行时,会标记内存中存储的所有变量,然后去掉上下文中变量的标记。未被去掉标记的变量将被销毁并回收内存。

示例代码:

var m = 0, n = 19;
add(m, n);
console.log(n);
function add(a, b) {
  a++;
  var c = a + b;
  return c;
}

引用计数

语言引擎维护一张“引用表”,记录内存中所有资源的引用次数。如果一个值的引用次数为0,表示不再被使用,可以释放内存。但如果引用次数不为0,即使不再需要,垃圾回收机制也无法释放内存,导致内存泄漏。

示例代码:

const arr = [1, 2, 3, 4];
console.log('hello world');

三、常见内存泄漏情况

  1. 意外的全局变量:函数内部未声明的变量可能成为全局变量,导致内存泄漏。
    • 代码示例:
function foo(arg) {
  bar = "this is a hidden global variable";
}
  1. 定时器引起的内存泄漏:如果定时器回调函数中引用了外部变量,即使相关DOM元素被移除,定时器仍存在,导致内存泄漏。
    • 代码示例:
var someResource = getData();
setInterval(function() {
  var node = document.getElementById('Node');
  if (node) {
    node.innerHTML = JSON.stringify(someResource);
  }
}, 1000);
  1. 闭包引起的内存泄漏:闭包可以维持函数内局部变量,使其得不到释放。
    • 代码示例:
function bindEvent() {
  var obj = document.createElement('XXX');
  var unused = function () {
    console.log(obj, '闭包内引用obj obj不会被释放');
  };
  obj = null;
}
  1. 未清理的DOM元素引用:对DOM元素的引用未清理,即使DOM元素被删除,引用仍然存在,导致内存泄漏。
    • 代码示例:
const refA = document.getElementById('refA');
document.body.removeChild(refA);
console.log(refA, 'refA');
refA = null;
console.log(refA, 'refA');
  1. 事件监听引起的内存泄漏:未取消的事件监听可能导致内存泄漏。应使用 removeEventListener 取消不再需要的事件监听。

结论

即使有垃圾回收机制,仍需关注内存泄漏问题。对于不再使用的大空间值,应检查是否存在对其的引用,并手动解除引用。