5

假设您有一个组件,其工作是创建一些 DOM 节点(例如使用 jquery):

function PageFiller() {
}
PageFiller.prototype.fillPage = function () {
  this.dom = $("<div/>", {
    "class" : "hello",
    text : "Hello world"
  });
  $("body").append(this.dom);
}

假设另一个组件使用它来填充 DOM,而不保留对“PageFiller”的引用

function Main() {
}
Main.prototype.start = function () {
   var filler = new PageFiller();
   filler.fillPage();
};
var main = new Main()
main.start();

不认为在那之后(在同一范围内),有人粗暴地清除了 DOM:

$(".hello").remove();

这种情况下的内存布局是什么?我是否使用 PageFiller 实例泄漏了内存?

我有点不确定 PageFiller 实例在什么时候会被垃圾收集(如果它曾经发生过),因为它持有对表示不再存在的部分 DOM 的 jquery 对象的引用?还是它与 DOM 无关,因为据我了解,实际的DOM 对象与常规 JS 变量位于不同的堆中(除非我在这里弄错了..)

更新:如果对 PageFiller 的引用由 Main 对象保留,则情况会有所不同:

Main.prototype.start = function () {
    this.filler = new PageFiller();
    filler.fillPage();
}

在这种情况下,Main 对象具有对 PageFiller 对象的引用,而 PageFiller 对象本身持有对 JQ 对象的引用;在“删除”之后,JQ 对象仍然存在于内存中,但指向不存在的 DOM 节点,对吧?

我是否正确地说在原始示例中,只要 main.start() 完成:

  • 没有对象持有对 PageFiller 对象的引用,所以它会被垃圾收集,而 pageFiller.dom 对象没有被任何人引用,所以它也会被垃圾收集?
  • 实际的 DOM 元素显然仍然存在于内存中,但在一个单独的堆中,我真的不必担心

其他情况:

这次是一个小的变化,事件处理程序使事情复杂化:

PageFiller.prototype.fillPage = function () {
  var self = this;
  this.dom = ... // as usual 
  this.dom.click(function () {
    alert("Just clicked on dom generated by " + self);
  });
}

这一次,仅仅让 PageFiller 实例超出范围还不足以回收它的内存,因为它被点击处理程序中使用的闭包引用。我在这里泄露了内存吗?或者我可以相信 $("").remove() 调用来终止对闭包的引用,从而终止对 PageFiller 的最后一个引用?如果我在没有 jquery 的情况下删除了元素(使用本机 DOM API 吗?),情况会有所不同吗?

这可能一切都像我希望的那样工作,但我只是想说服自己没有内存可以从这种模式中泄漏。

谢谢。

4

1 回答 1

1

更新

删除filler.dom一旦main.start()完成将减少它所持有的节点的引用计数,因为它不拥有节点(即弱属性)。

当您再次调用 refcount 时实际节点被删除时,它$().remove()会减少到零并且可以被垃圾收集。

所以,AFAICT 你应该没什么好担心的。

于 2012-05-30T16:50:38.953 回答