我记录了以下 Chrome bug ,这导致我的代码中有许多严重且不明显的内存泄漏:
(这些结果使用 Chrome 开发工具的 memory profiler ,它运行 GC,然后对所有未垃圾收集的内容进行堆快照。)
在下面的代码中,someClass
实例被垃圾回收(好):
var someClass = function() {};
function f() {
var some = new someClass();
return function() {};
}
window.f_ = f();
但在这种情况下不会被垃圾收集(坏):
var someClass = function() {};
function f() {
var some = new someClass();
function unreachable() { some; }
return function() {};
}
window.f_ = f();
以及对应的截图:
如果对象被同一上下文中的任何其他闭包引用,则闭包(在本例中为 function() {}
)似乎保持所有对象“事件”,无论是否这个闭包本身甚至是可以到达的。
我的问题是关于其他浏览器(IE 9+ 和 Firefox)中闭包的垃圾收集。我对 webkit 的工具很熟悉,比如 JavaScript heap profiler,但是我对其他浏览器的工具知之甚少,所以我一直无法测试。
在这三种情况下,IE9+ 和 Firefox 会垃圾收集 someClass
实例吗?
最佳答案
据我所知,这不是错误,而是预期的行为。
来自 Mozilla 的 Memory management page :“截至 2012 年,所有现代浏览器都配备了标记和清除垃圾收集器。” “限制:对象需要明确地不可访问”。
在您失败的示例中, some
在闭包中仍然可以访问。我尝试了两种方法使其无法访问并且都可以工作。当你不再需要它时,要么设置 some=null
,要么设置 window.f_ = null;
它将消失。
更新
我已经在 Windows 上的 Chrome 30、FF25、Opera 12 和 IE10 中尝试过。
standard没有说任何关于垃圾收集的事情,但给出了一些应该发生的事情的线索。
- 第 13 节函数定义,第 4 步:“让闭包成为创建 13.2 中指定的新函数对象的结果”
- 第 13.2 节“范围指定的词法环境”(范围 = 闭包)
- 第 10.2 节词汇环境:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment.
An outer Lexical Environment may, of course, have its own outer Lexical Environment. A Lexical Environment may serve as the outer environment for multiple inner Lexical Environments. For example, if a Function Declaration contains two nested Function Declarations then the Lexical Environments of each of the nested functions will have as their outer Lexical Environment the Lexical Environment of the current execution of the surrounding function."
因此,函数将可以访问父级的环境。
所以,some
应该在返回函数的闭包中可用。
那为什么它不总是可用?
Chrome 和 FF 似乎很聪明,可以在某些情况下消除该变量,但在 Opera 和 IE 中,some
变量在闭包中可用(注意:查看此设置断点on return null
并检查调试器)。
GC可以改进以检测函数中是否使用了some
,但这会很复杂。
一个不好的例子:
var someClass = function() {};
function f() {
var some = new someClass();
return function(code) {
console.log(eval(code));
};
}
window.f_ = f();
window.f_('some');
在上面的例子中,GC 无法知道变量是否被使用(代码经过测试并且在 Chrome30、FF25、Opera 12 和 IE10 中工作)。
如果通过为 window.f_
分配另一个值来破坏对对象的引用,则会释放内存。
在我看来这不是错误。
关于javascript - JavaScript 闭包是如何被垃圾回收的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19798803/