2013-08-06 87 views
6

我正在審查這個演示的幻燈片:http://slid.es/gruizdevilla/memory閉包如何造成內存泄漏?

,並在幻燈片中的一個,該代碼呈現它創建一個內存泄漏的建議:

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

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

閉包可以是另一個內存泄漏的來源。瞭解封閉中保留了哪些引用。

請記住:EVAL是邪惡

能有人在這裏解釋一下這個問題?

+3

我不相信這是由同一演示文稿(其中所說的泄漏是「當一個程序反覆未能返回內存定義的內存泄漏它已經獲得臨時使用「),因爲它不會重複發生。但'largeStr'將會佔用大量內存,直到'a'超出範圍。 'eval()'也不是邪惡的,它幾乎總是這個工作的錯誤工具。 – nnnnnn

+0

@nnnnnn:特別是在這裏,其中'eval'似乎是用來防止靜態代碼分析這將允許垃圾收集器收集'largeStr'即使當返回函數的引用是活的。 – Bergi

+0

@MedicineMan:你是否「理解什麼引用保留在閉包*中?」或者不是? – Bergi

回答

2

好吧,讓我們來看看這裏發生了什麼;

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

+0

一個很好的解釋,應該是任何人閱讀你感興趣的答案的自我解釋。 –

5

如果而不是返回,做

eval(''); 

你返回一個傳遞函數它的參數

eval(n); 

然後有人可以調用a('largeStr')得到陣列,所以JavaScript解釋器不能垃圾回收日e數組。

口譯可能意識到

eval(''); 

相當於

; 

,但大多數都沒有足夠的智慧做,所以一旦他們看到eval他們停止允許封閉過的GC只要關閉是可達的。


內存泄漏發生時eval不能有效地獲取封閉了,因爲它的輸入的性質變量:

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

由於'x' + (n-1)不能產生JS的字符串引用largeStr沒有任何輸入可以導致largeStr被使用,但它仍然固定在內存中。


在行動中看到了整個事情,玩弄

var f = (function() { 
    var a = [,,,,,]; 
    return function (x) { return eval(x); }; 
    })(); 
alert(f('a.length')); 
+0

@Bergi,我修復了這個錯字。謝謝。 –

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 :)