2017-09-18 32 views
12

在Nicholas Zakas的書中,他解釋了在Javascript中使用引用計數進行垃圾回收時循環引用的問題。他用下面的例子:JavaScript參考計數中的循環引用

function problem(){ 
    var objectA = new Object(); 
    var objectB = new Object(); 

    objectA.someOtherObject = objectB; 
    objectB.anotherObject = objectA; 
} 

解釋說,這兩個對象將永遠不會分配給它們的內存被釋放,因爲它們具備的功能裏面他們兩個引用。我想澄清一下這是如何工作的。

顯然,每個對象有兩個引用。第一個對象同時指向objectAobjectB.anotherObject。所以每個對象的引用計數是2.但是當函數退出時會發生什麼?這在書中沒有真正描述。他說,只要對該值的引用被另一個值覆蓋,引用計數就會減少。我認爲這意味着:

function problem(){ 
    var objectA = new Object(); 
    var objectB = new Object(); 

    objectA.someOtherObject = objectB; 
    objectB.anotherObject = objectA; 
    objectA.someOtherObject = objectA; //<-- that if I were to do this, 
             //the reference count of the first object (A) 
             //would become 3, and 1 for the second object (B). 

} 

但是,當函數退出時會發生什麼?據我瞭解,objectAobjectB和它們各自的引用對象的屬性都將被銷燬,因此,這兩個對象的引用計數將減少2.我沒有看到Zacas的「循環引用問題」談論。有人能解釋他想說什麼嗎?

回答

11

據我所知,objectA和objectB以及它們各自相互引用的屬性都將被銷燬。

號的局部變量objectAobjectB會被破壞(因爲該函數範圍結束和沒有封閉引用這些變量)。這意味着變量引用的對象中的引用計數會分別減少。

如果對象的引用計數爲0,則該對象將被銷燬,並且其引用的所有其他事物都會使其計數遞減。但是對象的數量仍然是1 - 它們仍然是相互引用的 - 並且沒有任何東西被破壞。

+0

是否還有一個仍然依賴於引用計數的實現?根據https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management,這些對象將*在所有現代實現中被銷燬。 – rici

+0

@rici不,引用計數 - 至少在其天真形式 - 並不完全是因爲這個缺陷和[需要改進](https://en.wikipedia.org/wiki/Reference_counting#Dealing_with_reference_cycles)。正如MDN所述,所有現代引擎都使用一些標記和掃描實現。然而,引用計數仍然可以應用於已知不是或不是非常循環的特定局部結構,因爲它的簡單性可能更好。 – Bergi

+1

是的,這就是我的想法。我認爲OP中提到的這本書有些過時,因爲我認爲MSIE的早期版本遭受了這個問題。但我的印象是現代ES實現將在本例中正確地垃圾收集這兩個對象。我的理解是,問題是在ref-counting環境中用循環引用來解釋問題,但肯定值得一提的是,問題在實踐中不再存在;沒有必要手動中斷參考週期,這與您仍然會發現浮動的建議相反。 – rici

10

帶引用計數和「循環」引用的「問題」出現在分配的對象包含對其他對象的引用時,但在其他方面未被活動分配引用時。也就是說,項目的整體參照圖中有一些派系不活躍,但仍包含對其他非活動項目的參考。

在你的示例代碼,當函數退出沒有活躍引用兩個分配的對象,但對象相互引用,所以引用計數不爲0的局部變量objectAobjectB將在消失函數的退出(因爲閉包本身就是垃圾),但是對象保持的內部引用保持其引用計數大於零。

這不是一個無法解決的問題,但它使作爲垃圾收集技術的其他簡單的引用計數方法複雜化。

請注意,沒有規定或規範堅持JavaScript實現使用任何特定的垃圾收集技術。

+0

謝謝你的回答。還有一個問題:如果'objectA'和'objectB'被銷燬,它們的基礎屬性怎麼不會被它們破壞?我是否將局部變量(即引用?)與內存中的實際對象混淆? – Sahand

+2

*變量*'objectA'和'objectB'被銷燬。將局部變量視爲調用該函數時創建的「影子」對象的屬性。當函數退出時,該對象沒有對它的引用,所以它的屬性消失。所有這一切意味着由局部變量引用的對象的引用計數遞減1。 – Pointy

+2

「有派系......」 - 你的意思是「週期」,而不是?集團(完整的子圖)確實表明了這個問題,但它們不是必需的。 – chi