2015-10-17 225 views
2

我有一個關於垃圾收集如何在Javascript中工作的快速問題。Javascript內存釋放

如果我有這樣的代碼:

var c = {name : ‘Bob’}; 
c = {name : ‘Mary’}; 

在變量c上面的代碼所指向的對象{名稱:「鮑勃」}。但是,我設置c指向內存中的另一個對象{name:'Mary'}。該c最初指向的對象({name:'Bob'})會發生什麼?由於沒有引用它,該原始對象是否會在內存中被釋放?

在另一種情況:

var c = {name : ‘Bob’}; 
d = c; 
c = {name : ‘Mary’}; 

現在,原來的對象是c是指向({名稱:「鮑勃」}):「鮑勃」不會因爲d仍然指向{名稱被釋放}即使在「c」被改爲指向新對象之後:{name:'Mary'}。正確?

所以基本上,只要仍有指向它的引用,對象將不會從內存中釋放。

有人請向我解釋,如果我正確地思考這個問題嗎?

+2

是的,原則上這是正確的。但是,*發生垃圾收集時完全取決於瀏覽器,因此經常使用表達式「可用於垃圾回收」而不是「垃圾回收」。 – RobG

回答

2

你有正確的想法,但也有一些細微之處是需要注意的:

首先,JavaScript運行時決定實際運行垃圾回收程序。未使用的對象是,標記爲垃圾回收,未立即收集。 GC可能非常昂貴,因此它不會持續運行。

其次,當一個對象變成unreachable時,它變得適用於GC,而不是簡單地沒有引用。

如果GC僅考慮引用計數,則可以創建無法訪問的無法收集的對象的「閉環」。

考慮這個片斷:

var owner = { name: 'Ann' }; // Let's call this object 'Ann' 
var pet = { name: 'Zizi' }; // And this one 'Zizi' 

// create a loop of references 
owner.pet = pet; 
pet.owner = owner; 

owner = null; // owner no longer refers to Ann 
pet = null; // pet no longer refers to Zizi 

當這個代碼運行結束,沒有頂層引用ZiziAnn - 他們是可達。在現代運行時(如瀏覽器中的運行時)中,它們被標記爲GC,並在下一次運行GC例程時被清除。

但是,如果只有當對象的引用次數達到零時才收集對象,該怎麼辦?我們來考慮一下Zizi。它不能收集,因爲Ann仍然有一個參考。它不能使用,因爲沒有可用的引用。

Ann也不能收集,因爲Zizi引用它。這是一個糟糕的情況 - 用戶代碼無法達到的兩個對象,但也無法收集垃圾。這是內存泄漏。

這種垃圾收集算法,被稱爲引用計數的,在舊版本的Internet Explorer引起了infamous issue:DOM節點和事件處理程序可以阻止對方從不斷被垃圾收集。因爲這個原因,參考計數垃圾收集器基本上已經過時。

延伸閱讀:Chrome Devtools Docs - Memory 101

+0

嗯,好的!我發現鏈接有點混亂哈哈...也許是因爲我不熟悉內存管理,但我有一個關於你的代碼的問題。當您設置owner = null和pet = null時,該行會做什麼?它是否會釋放所有者和寵物對象?你是不是也只是在你的例子中將鮑勃稱爲所有者?我不確定鮑勃來自哪裏哈哈 – LP496

+1

對不起,鮑勃從早先的編輯中遺留下來!我已經更新並希望現在澄清。賦值爲null不會立即釋放對象,但它確實會使它們無法訪問,因爲'owner'和'pet'不再引用它們。你不能在這個程序結尾編寫代碼,以某種方式獲得對'Ann'或'Zizi'的引用。因爲它們無法訪問,所以它們被標記爲GC(在現代運行時中)。 – joews

+0

哦,我終於明白了無法達到的概念!另一位用戶@Leo Nix發佈了一篇文章,指出JS使用標記和掃描算法,而不是引用計數來修復引用的內存泄漏問題。感謝您澄清! – LP496

2

正確的對象最終是垃圾收集,當沒有對它們的引用時,假設你的代碼片段在全局範圍內,這是真的,當然如果你的var聲明在一個函數內,它將超出函數的範圍已經退出。此例外是閉包,其中本地作用域上的對象仍由該函數返回的變量引用。

我上面提到'最終'的垃圾收集,因爲實際收集對象時會受到各種因素(內存壓力等)的影響,這是特定於所使用的JavaScript引擎(V8,脈輪,硝基等)。

1

MDN有關於memory management的一篇非常好的文章。它具體討論了內存分配和垃圾回收的例子。

+0

哦,這絕對清理了很多。我只是有一個快速的問題:在限制:循環在該函數中的頭每個對象引用對方。如果我們在該函數中調用return之後使用標記和掃描算法,那麼「2個對象(o和o2)不再被全局對象可訪問的東西引用」?是否因爲該函數的執行堆棧被釋放了? – LP496

+0

這兩個變量/對象的作用範圍是本地的,所以一旦函數執行完畢,它們就不能再被任何東西訪問,變得毫無用處。因此,標記和掃描可以釋放它們,而引用計數表明每個引用都被引用過一次,因此不符合垃圾回收的條件。 –

+0

啊,好的!我剛剛意識到這一點。你非常喜歡這個鏈接!幫了一噸! – LP496