9

I was reviewing the slides in this presentation: http://slid.es/gruizdevilla/memory

and on one of the slides, this code is presented with the suggestion that it creates a memory leak:

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

   return function (n) { 
        eval(''); //maintains reference to largeStr
        return smallStr; 
   }; 
}();

Closures can be another source of memory leaks. Understand what references are retained in the closure.

And remember: eval is evil

Can someone explain the issue here?

4

3 回答 3

7

如果不是返回一个函数

    eval('');

你返回了一个通过它的论点

    eval(n);

然后有人可以调用a('largeStr')来获取数组,因此 JavaScript 解释器无法垃圾收集数组。

口译员可以意识到

eval('');

相当于

;

但是大多数人都不够聪明,所以一旦他们看到eval他们就停止允许封闭变量的 GC,只要闭包是可达的。


eval当由于输入的性质而无法有效地访问封闭变量时,就会出现内存泄漏:

eval('x' + (n-1));

由于'x' + (n-1)无法生成不引用largeStr任何输入的 JS 字符串,因此可能会导致largeStr被使用,但它仍被固定在内存中。


要查看整个操作,请使用

 var f = (function () {
     var a = [,,,,,];
     return function (x) { return eval(x); };
   })();
 alert(f('a.length'));
于 2013-08-06T22:28:34.673 回答
4

好的,让我们考虑一下这里发生了什么;

var a = (function () { // `a` will be set to the return of this function
   var smallStr = 'x',
   largeStr = new Array(1000000).join('x'); 

   return function (n) { // which is another function; creating a closure
        eval('');
        return smallStr; 
   }; 
}());

内部函数需要能够访问外部函数中的所有变量,这意味着只要对它的引用存在,外部函数中的变量就不能被垃圾收集,因此在完成调用后继续消耗内存,因此可能导致“内存泄漏”。

如果您正在处理这样的大数据并且您已经完成了它,请将其设置为null

于 2013-08-06T22:14:57.870 回答
0

让我们稍微重写一下代码:

function makeClosure() { 
  var smallStr = 'x',
  largeStr = new Array(1000000).join('x'); 

   return function (n) { 
        eval(''); //maintains reference to largeStr
       return smallStr; 
  }; 
}

var a = makeClosure();
assert(a() === 'x');

makeClosure返回一个函数,因此a是一个函数。但是,该函数仍然在定义它的范围内执行(即闭包的定义)。如果你要这样做:

function makeEnumerator() {
  var count = 0;

  return function () {
    count++;
    return count;
  };
}

var enum = makeEnumerator();
assert(enum() === 1);
assert(enum() === 2);

enum仍然可以访问count. 回到我们的例子,闭包保留了对 的引用smallStr,它保留在内存中。largeStr不被保留,应该被释放。

但是,eval也在当前范围内执行并且可能使用largeStr. 因此,浏览器也被迫保留largeStr

长话短说,不要使用 eval :)

于 2013-08-06T22:30:01.533 回答