[英]Garbage Collection in 2.1 Cracking the Coding Interview
對於此代碼塊(破解編碼面試中2.1的解決方案):當您執行prev.next = n.next
,n是否會被垃圾收集器收集? 那你怎么辦n = n.next
?
2.1的問題是:編寫代碼以從未排序的鏈表中刪除重復項。
有人可以在這種情況下向我解釋垃圾收集器如何工作嗎?
public static void removeDup (LinkedListNode del)
{
LinkedListNode prev = null;
Hashtable myTable = new Hashtable();
while(del!= null)
{
// table does not have the key yet
if (myTable.containsKey(del.data) == false)
{
myTable.put(del.data, true);
prev = del;
}
// table has the duplicate
else
{
prev.next = del.next;
}
del = del.next;
}
}
當您調用prev.next = n.next;
, n
仍可立即用作方法中的變量。 它無處不在,處在生命的黃金時期。 僅當絕對無法訪問對象時,對象才會被垃圾回收。
如果要立即跟進,則n = null;
,那么可以肯定,對象n
指向的對象可能會被垃圾回收(假設您沒有將其存儲在其他任何地方)。 不是馬上就引起您的注意,但最終。 關鍵是,什么時候進行垃圾回收都沒有關系,因為您不再有任何方法可以將對該對象的引用返回到您的代碼中。
至於垃圾收集器,當你寫
del = del.next;
無論先前被刪除引用(假設沒有其他引用它)有資格被垃圾收集-如果變量del
是引用數據的唯一的事情,一旦del
被重新分配什么會引用了數據,這使得資格GC。 請注意,這並不意味着將立即進行GC處理。
順便說一句,在您的代碼中,您編寫的位置
else
{
prev.next = del.next;
}
您將獲得NullPointerException
,因為您正試圖訪問prev的成員,該成員已在上面設置為null。
您重新分配del
在你的方法, del = del.next;
。 因此,如果沒有其他直接或間接指向del
引用,則較舊的del
可能有資格進行垃圾回收。
當對象超出范圍且沒有其他引用指向該對象時,將對其進行垃圾回收。
我覺得,如果我們采用簡單的案例並逐步了解正在發生的事情,則更容易理解(以一種簡單的方式,實際的實現很可能使用計數器和更復雜的數據結構,但我偏離了方向)。 假設我們有一個鏈表1 -> 1 -> 2 -> null
傳遞給此方法,並在進入while循環之前先查看一下堆棧和堆上的內容。第一次:
Heap: {
#1 : { LLN, data: 1 , next #2 },
#2 : { LLN, data: 1 , next #3 },
#3 : { LLN, data: 2 , next: null },
#4 : { HashTable, keyvalues: {} }
}
Stack : {
del: #1
prev: null
myTable: #4
}
從這張圖中可以忽略的是,堆棧中用於調用函數的局部變量一直返回到鏈的頂部,當然還有其他我們不知道的分配對象。
我們也知道一個對象(分配在堆上)在任何代碼都無法訪問時才有資格進行垃圾回收。 換句話說:如果我不可能從堆棧上的引用開始,然后跟隨堆上的引用到達特定位置,則垃圾收集器有資格清除它。
讓我們在while循環中進行一次迭代(您應該結束此循環):
Heap: {
#1 : { LLN, data: 1 , next: #2 },
#2 : { LLN, data: 1 , next: #3 },
#3 : { LLN, data: 2 , next: null },
#4 : { HashTable, keyvalues: {1:true} }
}
Stack : {
del: #2
prev: #1
myTable: #4
}
仍然像以前一樣,一切都可以實現。 讓我們進入第二個迭代,結束else塊,然后執行do: prev.next = del.next
。 由於del.next =#3,因此我們為prev.next分配該值並得出以下結論:
Heap: {
#1 : { LLN, data: 1 , next: #3 },
#2 : { LLN, data: 1 , next: #3 },
#3 : { LLN, data: 2 , next: null },
#4 : { HashTable, keyvalues: {1:true} }
}
Stack : {
del: #2
prev: #1
myTable: #4
}
此時,您可以看到堆上的所有內容仍然有效(不符合垃圾回收的條件)。 #1被prev引用,#2被del引用,#4被myTable引用,並且#3到#1和#2都處於活動狀態。 現在看看當我執行del = del.next
時會發生什么:
Heap: {
#1 : { LLN, data: 1 , next: #3 },
#2 : { LLN, data: 1 , next: #3 },
#3 : { LLN, data: 2 , next: null },
#4 : { HashTable, keyvalues: {1:true} }
}
Stack : {
del: #3
prev: #1
myTable: #4
}
現在,我堆棧上的所有東西(未知)都沒有指向#2,因此此時它可以進行垃圾收集。 正如我之前提到的,我們不了解堆棧的其余部分,因此可能有其他引用它,但是很有可能它已經死了並且可以回收。 正如其他人已經提到的那樣,此時不必進行垃圾回收,但是如果確實如此,並且沒有其他東西持有對#2的引用,則它可以回收#2。
如果繼續進行此練習,您會注意到在while循環的下一次迭代中,#1也將不再被我們從堆棧中了解的任何內容所引用。 但是,調用此方法的方法很有可能具有一個引用#1的局部變量,因此,那時它可能不符合進行垃圾收集的條件。
當然,這是對發生的事情的非常簡化的瀏覽,但這是事物的本質。 如果您有興趣,還應該研究終結器在實際回收內存時如何發揮作用。 軟引用和弱引用也是非常有趣的主題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.