内存泄漏、垃圾回收机制、哪些情况会导致内存泄漏

时间:2021-03-17 20:55:23

http://www.ruanyifeng.com/blog/2017/04/memory-leak.html

什么是内存泄漏?
内存泄漏是指不再用到的内存,没有及时释放。既不能使用,又不能回收。

程序的运行需要内存。对于持续运行的进程,如果不及时释放不再用到的内存,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

要了解 JS 内存泄漏的几种情况,我们首先来了解一下 JS 的内存是如何管理的,即 JS 的垃圾收集机制。

垃圾收集机制
Javascript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中的使用的内存。所需内存的分配 以及无用的回收 完全实现了自动管理。

JavaScript 垃圾回收机制很简单:找出不再使用的变量,然后释放掉其占用的内存。但是这个过程不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。

JavaScript 中最常用的垃圾收集方式有 2 种:标记清除和引用计数。

1)“标记清除”——当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。当变量离开环境时,这将其 标记为“离开环境”。

从逻辑上讲,永远不能释放进入环境的变量所占的内存,因为只要执行流进入相应的环境,就可能用到它们。

function fun(){
var a = 1;//被标记 进入环境
var b= 2;//被标记 进入环境
}
fun();//执行完毕之后,a,b 被标记离开环境,被回收

如果我们的代码写法不当,会让变量一直处于“进入环境”的状态,无法被回收。

2)“引用计数”——语言引擎有一张”引用表”,跟踪记录每个值被引用的次数。
如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。

如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

那些很占空间的值,一旦不再用到,你必须检查是否还存在对它们的引用。如果是的话,就必须手动解除引用。

function test(){
var a = {} ; //a的引用次数为0
var b = a ; //a的引用次数加1,为1
var c = a; //a的引用次数再加1,为2
var b ={}; //a的引用次数减1,为1
}

导致内存泄漏的几种情况
1. 意外的全局变量
function leaks(){
leak = 'xxxxxx';//leak 成为一个全局变量,不会被回收
}

调用完函数以后,变量仍然存在,导致泄漏.
你可以通过加上 ‘use strict’ 启用严格模式来避免这类问题, 严格模式会阻止你创建意外的全局变量.

2. 闭包
闭包可以维持函数内局部变量,使其得不到释放。
解决办法:在函数外部定义事件处理函数,解除闭包。或在闭包中,删除没用的属性以减少对内存的消耗。或在外部函数中删除对DOM的引用

function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
obj=null;
}

3. 未清除 dom 元素的引用
dom 元素移除,但 对 dom 元素的引用没有解除,会导致内存泄漏。
解决办法:手工移除。

var elements = {
button: document.getElementById('button'),
image: document.getElementById('image')
}

function doStuff() {
image.src = 'http://some.url/image';
button.click();
console.log(text.innerHTML);
}

function removeButton() {
document.body.removeChild(document.getElementById('button'));
// 虽然我们用removeChild移除了button, 但是还在elements对象里保存着#button的引用
// 换言之, DOM元素还在内存里面.
}

4.循环引用
循环引用 在引用计数策略下会导致内存泄漏,标记清除不会。
解决办法:手工解除循环引用。

function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();

a和b的引用次数都是2,fn()执行完毕后,两个对象都已经离开环境。
在标记清除方式下是没有问题的,但是在引用计数策略下,a和b的引用次数不为0,不会被垃圾回收器回收内存。如果fn函数被大量调用,就会造成内存泄漏。

IE中的BOM和DOM中的对象使用C++以COM(component Object Model,组件对象模型)对象的形式实现而COM对象的垃圾收集机制采用的是引用计数策略。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e = element;
element.o = myObject;
//手工断开它们之间的连接
myObject.element=null;
element.someObject=null;

5. 被遗忘的计时器或回调
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);

原文:https://blog.csdn.net/meijory/article/details/76839072
版权声明:本文为博主原创文章,转载请附上博文链接!