一、内存泄漏定义
内存泄漏是指程序未能释放不再使用的内存,导致内存浪费。在持续运行的服务进程中,不及时释放内存可能导致系统性能下降或进程崩溃。
二、垃圾回收机制
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');
三、常见内存泄漏情况
- 意外的全局变量:函数内部未声明的变量可能成为全局变量,导致内存泄漏。
- 代码示例:
function foo(arg) {
bar = "this is a hidden global variable";
}
- 定时器引起的内存泄漏:如果定时器回调函数中引用了外部变量,即使相关DOM元素被移除,定时器仍存在,导致内存泄漏。
- 代码示例:
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if (node) {
node.innerHTML = JSON.stringify(someResource);
}
}, 1000);
- 闭包引起的内存泄漏:闭包可以维持函数内局部变量,使其得不到释放。
- 代码示例:
function bindEvent() {
var obj = document.createElement('XXX');
var unused = function () {
console.log(obj, '闭包内引用obj obj不会被释放');
};
obj = null;
}
- 未清理的DOM元素引用:对DOM元素的引用未清理,即使DOM元素被删除,引用仍然存在,导致内存泄漏。
- 代码示例:
const refA = document.getElementById('refA');
document.body.removeChild(refA);
console.log(refA, 'refA');
refA = null;
console.log(refA, 'refA');
- 事件监听引起的内存泄漏:未取消的事件监听可能导致内存泄漏。应使用
removeEventListener取消不再需要的事件监听。
结论
即使有垃圾回收机制,仍需关注内存泄漏问题。对于不再使用的大空间值,应检查是否存在对其的引用,并手动解除引用。