JavaScript内存泄漏及常见方式

时间:2022-08-04 14:57:10

一 内存管理



程序的运行需要内存。只要程序提出要求,操作系统或者运行时( runtime)就必须供给内存。对于JavaScript来说,有栈内存( stack)和堆内存( heap)。

对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

不再用到的内存,没有及时释放,就叫做内存泄漏memory leak)。

有些语言(比如 C 语言)必须手动释放内存,程序员负责内存管理。

char * buffer;
buffer = (char*) malloc(42);

// Do something with buffer

free(buffer);

上面是 C 语言代码,malloc方法用来申请内存,使用完毕之后,必须自己用free方法释放内存。

这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为”垃圾回收机制“(garbage collector)。

二 内存生命周期


  1. 分配需要的内存
  2. 使用它(读、写)
  3. 当它不使用时释放



三 js中的垃圾回收机制


  1. 最常用的:标记清除,“对象不再需要”定义为“对象是否可以获得”
  2. 引用计数,“对象不再需要”定义为“没有其他对象引用到它”
    问题:循环引用
function problem(){
    var obj1=new Object();
    var obj2=new Object();

    obj1.a=obj2;
    obj2.a=obj1;
}
problem();

在上述代码中,函数problem调用后,obj1,obj2已经不再需要了,但是由于obj1与obj2相互引用,它们的引用次数至少为1,所以它们不会被回收。

IE中的BOM和DOM中的对象是以C++COM(组件对象模型)的形式实现的,而COM对象的垃圾收集机制采用的就是引用计数。即使IE的JavaScript引擎是使用的标记清除来实现的,JavaScript对象依然是基于引用计数策略的。所以存在循环引用的问题。

var element=document.getElementById("id");
var obj=new Object();
obj.element=element;
element.someObject=obj;

上述代码中,DOM元素和一个原生JavaScript对象相互引用。由于循环引用,即使将该DOM从该页面中移除,它也永远不会被回收。

怎么解决?手动断开原生JavaScript与DOM元素之间的连接。

obj.element=null;
element.someObject=null;

手动断开后,当垃圾收集机制再次运行时会删除这些值并回收它们的内存。

IE9BOMDOM对象都转换成了真正的JavaScript对象。

四 常见的内存泄漏的方式


  • 意外的全局变量(变量未声明直接复制,this对象)
  • 被遗忘的计时器或者回调函数
  • 没有清除的DOM对象的引用
  • 闭包

五 调试工具



可以通过Chrome Debug Tool来查看

  • timeline 可以检测代码中不需要的内存。
  • profiles 可以记录当前的堆内存(heap)快照,并生成描述文件。

参考:如何处理 JavaScript 内存泄露