Java垃圾回收機制—幾種回收方式的介紹(學習筆記)

  • 垃圾回收器如何工作

       它像一個傳送帶,每分配一個新對象,它就往前移動一格。這意味着對象存儲空間的分配速度非常快。Java的“堆指針”只是簡單地移動到尚未分配的區域,其效率比得上C++在堆棧上分配空間的效率。當它工作時將一面回收空間,一面使堆中的對象緊湊排列,這樣“堆指針”就可以很容易移動到傳送帶的開始處,也就避免了一些錯誤的發生。通過垃圾回收器對對象重新排列,實現了一種高速的、有無限空間可被分配的堆模型。

  • 實現

  • 引用計數

       這是一種很簡單但是速度很慢的方垃圾回收技術。每個對象都含有一個引用計數器,當有引用鏈接到對象時,計數器加1,當有引用離開作用域或者置爲null時,計數器減1.雖然管理引用計數的開銷不大,但是這項開銷將存在整個程序的生命週期中,將會持續發生。垃圾回收器會在含有全部對象的列表上進行遍歷,當發現某個對象的引用計數爲0時,就釋放其佔用的內存空間。這種方法有個缺陷,就是對象之間可能存在循環引用,可能會出現“對象應該被回收,但是引用計數器不爲零”,對於垃圾回收器而言定位這樣的交互自引用的對象組所需的工作量極大。所以引用計數常用來說明垃圾收集的工作方式,但是卻從未被應用於任何一種Java虛擬機的實現中。

  • 停止—複製and標記—清掃

       停止-複製意味着先暫停程序,然後將所有存活的對象從當前堆複製到另一個堆,沒有被複制的全部都是垃圾。當對象被複制到新堆時,它們是一個挨着一個的保持着緊湊的狀態,所以可以直接分配新空間了。

       當把對象搬到另一處的時候,所有指向它們的引用都需要修改,位於堆和靜態存儲區的引用可以直接修改,但是可能還有其他指向這些對象的引用,需要在遍歷的過程中才能被找到(可以想象成一個表格,將舊地址映射到新地址)。

       對於這種所謂的“複製式回收器”而言,效率會低下,這其中有兩個原因。

       1:得有兩個堆,然後得在這兩個分離的堆之間來回倒騰,從而得維護比實際需要多一倍的空間(某些Java虛擬機的處理方式 是,按需從堆中分配幾塊較大的內存,複製動作發生在這些大塊內存之間)。

       2:第二個問題在於複製。程序進入穩定狀態後,可能只產生少量的垃圾,甚至沒有垃圾。儘管如此,複製式回收器仍會將所有的內存自一處複製到另一處,這很浪費。爲了避免這種情況就會轉換爲另一種工作模式(自適應)。這種模式成爲標記-清掃,標記-清掃方式的速度相當慢,但是當你知道只會產生少量垃圾甚至不會產生垃圾時,它的速度就很快了。

       “標記-清掃”所依據的思路同樣是從堆棧和靜態存儲區出發,遍歷所有的引用,進而找出所有存活的對象,就會給對象設一個標記,這個過程不會回收任何對象。只有全部標記完成的時候,清理工作纔會開始。在清理過程中,沒有被標記的對象將會被釋放,不會發生任何複製的動作。所以剩下的空間是不連續的,垃圾回收器要是希望得到連續的空間的話,就需要重新整理剩下的對象。

       在這裏討論Java虛擬機中,內存分配以較大的“塊”爲單位。如果對象較大,它會佔用單獨的塊。嚴格來說,“停止-複製”要求在釋放對象前,必須先把存活的對象從舊堆複製到新堆,這將導致大量的內存複製行爲。有了塊之後,垃圾回收器在回收的時候就可以往廢棄的塊裏拷貝對象了。每個塊都用相應的代數來記錄它是否還存活。通常,如果塊在某處被引用,其代數就會增加;垃圾回收器將對上次回收之後的塊進行整理。這對處理大量短命的臨時對象很有幫助。垃圾回收器會定期進行完整的清理動作——大型對象仍然不會被複制(其代數將會增加),內含小型對象的那些塊則被複制並並整理。Java虛擬機會進行監視,若果所有的對象都很穩定,垃圾回收器的效率降低的話,就切換到“標記-清掃”方式;同樣,要是空間出現很多碎片,就會切回“停止-複製”的方式。這就是“自適應”技術。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章