2

我在关闭部分阅读 Addy Osmani内存高效 JS在粉碎杂志上的博客。我了解以下函数包含对 largeStr 的引用,并且 GC 不能声明它。

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

而且他在这里提到的解决方案没有引用 largeStr 并且 GC 可以声明它。相反,他使用 smallStr。

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

我明白了艾迪的观点,即不提及大事。但是,我想知道是否有任何(更好的)方法,我可以拥有第一个函数的功能并使其内存高效。

4

2 回答 2

9

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

第二个函数没有持久引用,largeStr因此垃圾收集器可以释放它。

听起来您在问是否有办法保留对大型事物的引用,但不使用该内存。对此的答案是否定的。

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


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

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

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()不必重新创建大字符串,但是单个 largeStr 在创建后永远不会被垃圾收集。

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

于 2013-10-21T21:40:15.170 回答
1

您需要非常谨慎的设置来导致 V8 闭包内存泄漏:

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代码无法访问,但在您完全摆脱之前无法收集window.a

http://jsfiddle.net/bwPVe/

如果你在 chrome 中做堆快照,你会看到。

于 2013-10-22T16:36:11.950 回答