簡體   English   中英

2.1破解編碼面試中的垃圾收集

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM