1. 程式人生 > >Java 垃圾回收機制(早期版本)

Java 垃圾回收機制(早期版本)

重新 速度 交互 綜合 技術 -1 遍歷 我們 後臺

Java 垃圾回收機制在我們普通理解來看,應該視為一種低優先級的後臺進程來實現的,其實早期版本的Java虛擬機並非以這種方式實現的。

先從一種很簡單的垃圾回收方式開始。

引用計數

  引用計數是一種簡單但是速度很慢的垃圾回收技術。

  每個對象都含有要給引用計數器,當有引用連接至對象時,引用計數+1。

  當引用離開作用域或者被置為null時,引用計數-1。

  當發現某個對象的引用計數為0時,就釋放其占用的空間。

  技術分享

   這種方法開銷在整個程序生命周期中持續發生,並且該方法有個缺陷,如果對象之間存在循環引用,可能會出現 “對象應該被收回,但引用計數卻不為零” 的情況。

   即

b.setA(a);
a.setB(b);

   對垃圾回收器而言,定位這樣的交換自引用的對象組所需的工作量極大。引用計數常用來說明垃圾收集的工作方式,但似乎未被應用於任何一種Java虛擬機實現中。


如何解決

  很多垃圾回收器並非基於引用記數計數。它們依賴的思想是:

    對任何“活”對象,一定能最終追溯到其存活再堆棧或者靜態存儲區之中的引用。這個引用鏈條可能會穿過數個對象層次。由此,如果從堆棧和靜態存儲區開始,遍歷所有的引用,就能找到所有“活”的對象。對於發現的每個引用,必須追蹤它所引用的對象,然後是此對象包含的所有引用,如此反復進行,知道“根源於堆棧和靜態存儲區的引用”所形成的網絡全部被訪問為止。你所訪問過的對象必須是“活”的。

    註意,這就解決“交互自引用的對象組”的問題——這種現象根本不會被發現,因此也被自動回收了。


停止-復制

   顯然,意味著,這種方法要先暫停程序運行(所以他不屬於後臺回收模式),然後將所有存活的對象從當前堆棧復制到另個堆(所有指向它的引用都要被重新修正),沒有被復制的全部都是垃圾。

   當對象被復制到新堆時,他們時一個挨著一個的,所以新堆保持緊湊配列,然後就可以根據前述方法,直接地分配新空間了。

   存在的問題:效率太低。

    (1)在兩個堆之間倒騰,需要維護多一倍的空間;

    (2)程序進入穩定狀態後,可能只產生少量垃圾,復制式回收器仍然會將所有內存自一處復制到另一處,這很浪費。

   為了解決第二個問題,產生了“標記-清掃”

標記-清掃

  從堆棧和靜態存儲出發,遍歷所有引用,進而找出所有活的對象,並標記,整個過程不會回收任何對象。

  只有全部標記工作完成的時候,清理動作才會開始。在清理過程中,沒有標記的對象將被釋放,不會發生任何復制動作。

  但此方法同樣必須在程序暫停的情況下才能進行。

  綜合兩種方法,即可形成早期Java回收機制——“自適應”技術

  顧名思義,Java虛擬機會進行監控,如果對象都很穩定,垃圾回收器的效率降低時,就切換“標記-清理”方式;

  同樣,Java虛擬機會跟蹤“標記-清理”的效果,要是堆空間出現很多碎片,就會切換回“停止-復制”方式。

   

  

Java 垃圾回收機制(早期版本)