JS 的循环引用, 原因是什么?

题叶
  • 17.3k

Mozilla 文档上的例子, 这个是在 IE 6,7 里的,
https://developer.mozilla.org/en-US/docs/JavaScript/Memory_Management

var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // The div has a reference to the event handler via its 'onclick' property
// The handler also has a reference to the div since the 'div' variable can be accessed within the function scope
// This cycle will cause both objects not to be garbage-collected and thus a memory leak.

这里说的, 函数里有一个对 div 的引用, 可是在哪, 是 this 么?
在 Chrome Firefox 里是否有类似的内存泄漏问题?
这是语言设计的失误么?

回复
阅读 16.3k
6 个回答

这和《Javascript高级程序设计》中函数表达式的一段很像

function assignHandler () {
  var element = document.getElementById('someElement');
  element.onclick = function () {
    alert(element.id);
  };
}

书上描述“由于匿名函数中引用了element,所以element的引用数最少是1,导致占用的内存永远不会被回收。”,把这个就叫内存泄漏。

建议写法

function assignHandler () {
  var element = document.getElementById('someElement');
  var id = element.id;
  element.onclick = function () {
    alert(id);
  };
  element = null;
}

个人觉得内存非常规的占用就应该算作内存泄漏,而不是非得无限增加的内存占用。

安坚实
  • 2.5k

那几句英文大概是这个意思:
1. 名叫 div 的变量有一个对 handler 的引用(因为 handler 是它的一个属性)
2. handler 也同样有一个对 div 的引用(因为 div 处于它的作用域内。这是js闭包的特性,函数内的代码可以引用函数外的变量)
3. 这就造成了循环引用,最后两个变量都不会被销毁,成了内存泄露

按照这个说法,像下面这样写代码就不会出现这种内存泄露了。
因为 div 变量不再处于 handler 函数的作用域之中。

var handler = function(){ dosomething(); };
(function(){
  var div = document.createElement("div");
  div.onclick = handler;
})();

不过我也是猜想的,没怎么研究过内存泄露的问题。

啥叫内存泄露,这几句代码绝逼不会引起内存泄露。。。。。

只是有内存泄露的风险。。。只是在内存里常驻了。。。其实js里很容易写出造成变量在内存里常驻的代码什么的。。写多了都没感觉了,最多多占用几B几KB。。。内存泄露是指不断循环引用,,,造成内存狂飙的那种代码。

写NodeJS的时候就对内存泄露有更多体会了,因为你一个方法可能会在瞬间被无数个请求激活调用,如果代码写的不好很容易就泄露了。。。不过V8也远比我们想象的聪明,它会自动帮我们处理掉一些情况,具体就木有研究过了。

说来说去,其实我也不是很懂,纯扯皮

https://developer.mozilla.org...

标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。

这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。

这个算法比前一个要好,因为“有零引用的对象”总是不可获得的,但是相反却不一定,参考“循环引用”。

从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。

循环引用不再是问题了
在上面的示例中,函数调用返回之后,两个对象从全局对象出发无法获取。因此,他们将会被垃圾回收器回收。第二个示例同样,一旦 div 和其事件处理无法从根获取到,他们将会被垃圾回收器回收。
限制: 那些无法从根对象查询到的对象都将被清除
尽管这是一个限制,但实践中我们很少会碰到类似的情况,所以开发者不太会去关心垃圾回收机制。

这是不是一个长生命周期的对象对一个短生命周期对象持有引用而导致对象无法回收 而内存泄露的问题。 在java里这种类似写法会有内存泄露的问题。

刚刚特意测试了一下,chrome里无此问题,只要这一堆东西在外部没有引用了,管它内部怎么互相引用,内存都不会增加(被GC了)。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏