仅由 'bound_this' 引用的实例不会被垃圾回收

Posted

技术标签:

【中文标题】仅由 \'bound_this\' 引用的实例不会被垃圾回收【英文标题】:Instances referenced by 'bound_this' only are not garbage collected仅由 'bound_this' 引用的实例不会被垃圾回收 【发布时间】:2012-07-19 13:18:25 【问题描述】:

我对 google chrome(版本 20.0.1132.47,Ubuntu 11.04 64 位)中的垃圾收集有疑问。

在比较堆转储和检查内存泄漏时,我发现了一些从未清理过的实例。通常这种行为可以追溯到程序员错误,但在这种情况下,我相当无能..

看看下面的截图

实例“child @610739”仅由属于子实例本身函数的“bound_this”实例引用。因此,据我了解,子实例应该被垃圾收集,因为保持它的唯一其他引用是子实例本身(通过 'bound_this' 函数)。

我正在使用 underscore.js 的 'bindAll' 实用函数 (underscore.js#bindAll),它映射到 chrome 的 'native_bind' 函数 (ECMA Script wiki on bound_this)

我是否在这里遗漏了一些明显的东西,如果有,有人可以解释是什么让这些实例保持活力吗?

更新: 与此同时,我在 chrominium (18.0.1025.168 (Developer Build 134367 Linux) Ubuntu 11.10) 中测试了相同的应用程序,它没有显示这些悬空实例..

更新 2: 按照 Esailijas 提示提供一个 jsfiddle sn-p,我创建了一个 (http://jsfiddle.net/8gSTR/1/) 来模仿我基本上正在做的事情。不幸的是,运行这个小提琴并没有显示我在我的应用程序中遇到的不当行为。尽管来自 window.o 数组的引用使实例保持活动状态,但在仍然引用“a”实例时进行的堆转储看起来有点相似:

在我的案例中缺少这样的引用(屏幕截图 1)我不知道是什么让 chrome 无法释放这些实例...

更新 3: 遵循 loislos 的建议以启用隐藏属性。结果(其中一个分支已展开)可以在下面的屏幕截图中看到,但它并没有让我更进一步。

【问题讨论】:

这里没有看到这个jsfiddle.net/uGX22/3。最初它们在那里(堆占用 17.32mb 和 60000 个闭包),我等了几分钟并拍摄了一个新快照,堆又回到了 6mb 并且闭包消失了。你能修改我的 jsfiddle 来重现这个吗? 你能告诉我们你创建这种情况的代码吗?我并没有真正从调试器的屏幕截图中得到它。 我将尝试创建一个小 sn-p 来重现该问题。我创建这种情况的代码是一个相当大的 SPA 的一部分,所以提取起来并不容易。 与此同时,我在 chrominium (18.0.1025.168 (Developer Build 134367 Linux) Ubuntu 11.10) 中测试了相同的应用程序,它没有显示这些悬空实例.. 能否请您在常规设置对话框中启用显示对象的隐藏属性复选框(它隐藏在 DevTools 右下角的齿轮图标下)并关闭/打开 devtools 窗口 【参考方案1】:

您怀疑这不是真正的内存泄漏,而是 chrome 的怪异是正确的。

我最近遇到了同样的问题。 IE11 不会将 this.func = _.bind(this.func, this) 显示为内存泄漏,而 chrome 会显示,即使您按下收集垃圾按钮 100 次。

即使你用 jsflag 废话运行 chrome 并暴露底层垃圾收集器并调用 gc 100 次,它也会显示它。

证明它实际上不是 chrome 泄漏的一种简单方法是将小问题变成浏览器的大问题并强制引擎采取措施。

在分配了绑定函数的实例上分配一个新属性,如下所示:

target.WhyAmILeaking = new Array(200000000).join("YOURNOT");

使用 chrome 执行三个快照技术,您会看到堆中的字符串以大约 214mb 的速度出现。再次执行任何建设性操作(第二个快照),您会看到堆达到 423mb 或保持在 214mb。如果它仍然存在,那么您就证明了原始的 214mb 已被收集。如果它没有留下来执行您的破坏性操作(第三个快照),它会回到 214mb,也证明原始已被收集。

如果它只是停留在 423mb,先生或女士有泄漏。

哦,还有一群遇到完全相同情况的可怜人:https://github.com/jashkenas/backbone/issues/2269#issuecomment-13610969

TL;DR;使用 IE 11 检测泄漏。

【讨论】:

【参考方案2】:

我在EcmaScript 中提到了这个,这个术语将这个与类“绑定”,

类方法具有“绑定 this”,其中方法体中的 this 始终绑定到从中提取方法的类实例,而不管此参数实际传递给方法的是什么(方法的 this 参数被忽略)。

现在在您的示例中没有给出示例脚本,因此不能针对问题,

我不得不说这不是泄漏..

然后对于任何对象,脚本是有界的,在类/对象存在之前它不会调用垃圾,因为这个函数与同一个对象绑定..它只会在其父对象(对象/类它是有界的)范围结束。

现在如果循环引用在任务完成时它会结束,>>这就像递归的概念(注意它的概念,不一样)......所以结束将从最后一次调用开始......相反..最里面的call(latest call)会先被释放..以此类推

代码必须是错误的,因为它们不能每次都绑定和定义函数和重用。但是如果它们是绑定的,那么它也应该可以正常工作并且在占用空间超出内存池的浏览器限制之前不应该泄漏。

我希望这会解释..

【讨论】:

【参考方案3】:

您是否正在使用开发人员工具触发“强制 GC”?如果是这种情况并且对象仍然存在,那么上述研究是有价值的。

如果您不是通过开发者工具触发 GC,请记住 GC 引擎的工作方式相当复杂,并且有分代收集的概念(使用两代)。

http://www.html5rocks.com/en/tutorials/memory/effectivemanagement/#toc-v8-gc

这可能只是因为您没有达到触发年轻代 GC 所需的阈值,或者对象已被老一代使用但从未触发过老一代 GC。

不幸的是,开发人员工具似乎没有显示 GC 是哪一代。

【讨论】:

以上是关于仅由 'bound_this' 引用的实例不会被垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章

SELECT、SELECT COUNT 和交叉引用表可以仅由一个查询处理吗?

Android实战——LeakCanary检测内存泄漏

在 AWS CloudFormation 模板中,如何使用自己的 Id 标记 EC2 实例而不会出现循环引用错误?

自动引用计数

JVM——GC

未将对象引用设置到对象的实例如何解决