首页 > 网站 > WEB开发 > 正文

浅析闭包和内存泄露的问题

2024-04-27 14:19:57
字体:
来源:转载
供稿:网友

浅析闭包和内存泄露的问题

javaScript使用一种称为垃圾收集的技术来管理分配给它的内存。这与C这样的底层语言不同,C要求使用多少借多少,用完再释放回去。其他语言,比如 Objective-C,实现了一个引用计数系统来辅助完成这些工作,我们能够了解到有多少个程序块使用了一个特定的内存段,因而可以在不需要时清除这些内存段。

Javascript是一种高级语言,它一般是通过后台来维护这种计数系统。

当JavaScript代码生成一个新的内存驻留项时(比如一个对象或函数),系统就会为这个项留出一块内存空间。因为这个对象可能会被传递给很多函数,并且会被指定给很多变量,所以很多代码都会指向这个对象的内存空间。JavaScript会跟踪这些指针,当最后一个指针废弃不用时,这个对象占用的内存会被释放。

   A ---------> B ------------> C

例如对象A有一个属性指向B,而B也有一个属性指向C。即使当前作用域中只有对象A有效,但由于指针的关系所有3个对象都必须保留在内存中。当离开A的当前作用域时(例如代码执行到声明A的函数的末尾处),垃圾收集器就可以释放A占用的内存。此时,由于没有什么指向B,因此B可以释放,最后,C也可以释放。

然而,当对象间的引用关系变得复杂时,处理起来也会更加困难。

   A ---------> B ------------> C                ^、_ _ _ _ _ _ _|

这里,我们又为对象C添加了一个引用B的属性。在这种情况下,当A释放时,仍然有来自C的指针指向B。这种引用循环需要由JavaScript进行特殊的处理,但必须考虑到整个循环与作用域中的其他变量已经处于隔离状态。

从这里我们可以看到,闭包问题的本质是作用域的问题,我平时写的闭包大多出现在:

循环引用

闭包可能会导致在不经意间创建循环引用。因为函数是必须保存在内存中的对象,所以位于函数执行上下文中的所有变量也需要保存在内存中:

function outerFn() {    var outerVar = {};    function innerFn() {        console.log(outerVar);    }    outerVar.fn = innerFn;    return innerFn;};

这里创建了一个名为 outerVar 的对象,该对象在内部函数innerFn()中被引用。然后,为 outerVar 创建了一个指向 innerFn()的属性,之后返回了innerFn()。这样就在 innerFn() 上创建了一个引用outerVar的闭包,而outerVar又引用了innerFn()。

这会导致变量在内存中存在的时间比想象得长,而且又不容易被发现。这还不算完,还有可能会出现比这种情况更隐蔽的引用循环:

function outerFn() {    var outerVar = {};    function innerFn() {        console.log('hello');    }    outerVar.fn = innerFn;    return innerFn;};

这里我们修改了innerFn(),不再招惹 outerVar。但是,这样做仍然没有断开循环引用。即使innerFn()不再勾引 outerVar,outerVar 也仍然位于innerFn()的封闭环境中。由于闭包的原因,位于 outerFn()中的所有变量都隐含地被 innerFn()所引用。我们再想一想,在 java 中的内部类不也是类似当前情况吗,内部类能够‘看’外部的 this。此时此刻,正如彼时彼刻,竟如此相像。因此,闭包会使意外地创建这些引用循环变得易如反掌。

DOM与JavaScript的循环

虽然我很早就知道闭包,也在调试内存问题时在 Chrome F12 里的 PRofile 是里看到 closure reference,但是并不清除这个问题的根源。因为上述情况通常不是什么问题,JavaScript能够检测到这些情况并在它们孤立时将其清除。

最近看到关于这个问题的解释:在旧版本IE中存在一种难以处理的循环引用问题。

当一个循环中同时包含DOM元素和常规JavaScript对象时,IE无法释放任何一个对象——因为这两类对象是由不同的内存管理程序负责管理的。

除非关闭浏览器,否则这种循环在IE中永远得不到释放。为此,随着时间的推移,这可能会导致大量内存被无效地占用。

导致这种循环的一个常见原因是简单的事件处理:

$(document).ready(function() {    var button = document.getElementById('button-1');        button.onclick = function() {            console.log('hell
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表