Javascript 解释Addy Osmani在smashing magazine中的闭合内存泄漏示例

Javascript 解释Addy Osmani在smashing magazine中的闭合内存泄漏示例,javascript,performance,garbage-collection,Javascript,Performance,Garbage Collection,我在《粉碎》杂志的结尾部分读到了艾迪·奥斯马尼的博客。我理解以下函数包含对Largester的引用,GC不能声明它 var a = function () { var largeStr = new Array(1000000).join('x'); return function () { return largeStr; }; }(); 他在这里提到的解决方案没有提到Largester,GC可以声称它。相反,他使用smallStr var a = functio

我在《粉碎》杂志的结尾部分读到了艾迪·奥斯马尼的博客。我理解以下函数包含对Largester的引用,GC不能声明它

var a = function () {
   var largeStr = new Array(1000000).join('x');
   return function () {
     return largeStr;
    };
}();
他在这里提到的解决方案没有提到Largester,GC可以声称它。相反,他使用smallStr

var a = function () {
    var smallStr = 'x';
    var largeStr = new Array(1000000).join('x');
    return function (n) {
        return smallStr;
    };
}();

我明白了艾迪的意思,他没有提到一件大事。但是,我想知道有没有更好的方法,我可以拥有第一个函数的功能,并使其内存效率更高。

第一个函数创建的
最大的
并返回引用它的函数。因此,合乎逻辑的是,垃圾收集器无法释放
largester
,因为现在包含在
a
变量中的函数仍在使用它

第二个函数没有对最大值的持久引用,因此垃圾收集器可以释放它

听起来你在问是否有一种方法可以保留对某个大对象的引用,但不使用该内存。答案是否定的

此外,从技术上讲,这些根本不是“泄漏”。它们是合法的内存使用


通过不预构建大字符串,可以在不占用内存的情况下实现第一个函数的功能。如果您根据需要构建它,那么在有人调用该函数之前,它不会消耗内存。这显然是执行速度和内存使用之间的直接折衷,但这是您在这里得到的选择。如果它是预缓存的,那么它会消耗内存。如果它只是根据需要构建的,那么它在使用之前不会消耗内存

以下是按需构建的版本,在使用之前不会消耗内存:

var a = function () {
   return function () {
     return new Array(1000000).join('x');
    };
}();
不必写得这么迟钝。也可以是这样,因为不涉及关闭:

var a = function() {
    return new Array(1000000).join('x');
}
这两个版本的缺点是每次调用
a()
时都会创建字符串,但优点是不会永久缓存任何内容。当
a()
的所有使用都完成时,所有内容都会被垃圾收集


或者,仅在首次使用时对其进行缓存:

var a = function () {
   var largeStr;
   return function () {
     if (!largeStr) {
         largeStr = new Array(1000000).join('x');
     }
     return largeStr;
    };
}();
这样做的好处是,在第一次调用
a()
之前不会消耗内存,对
a()
的后续调用不必重新创建大字符串,但创建后,将永远不会对单个最大字符串进行垃圾收集


哪一种最好取决于您的使用模式,哪一种权衡在您的设计/使用中更为重要。

您需要经过深思熟虑的设置才能导致V8 Close内存泄漏:

function create() {
    var a = "x";
    var b = new Array(1000000).join('x');

    //Force context-allocation for b
    (function(){b;});

    return function() {
        return a;
    };
}

window.a = create();
b
无法被代码访问,但在您完全摆脱
窗口之前,它无法被收集。a


如果您在chrome中使用堆快照,您将看到。

本文并不打算暗示这两个函数做相同的事情。它们只是例子。但我想,您可以将创建大字符串的代码移动到返回的函数中。但每次都会产生一个新的张力。一次又一次地产生相同的张力不是一个好主意。一个全局将更好(节省处理时间)。。。实际上,我在解释这个问题,想知道什么是最好的解决方案。这取决于字符串发生了什么。如果代码很少需要字符串,而且只需要很短的时间,那么最好每次都创建一个新副本。如果代码经常需要它,那么第一个版本可能还可以(而且比全局变量更好)。我想知道我是否遗漏了一些方面。谢谢你的回答。