JAVA GC算法詳解


生存還是死亡

對象是否需要被垃圾收集器回收主要有兩種方式:引用計數法可達性分析算法

引用計數法

給對象添加一個引用計數器,每當有一個地方引用他的時候,計數器的數值就+1,當引用失效時,計數器就-1;任何時候計數器的數值都為0的對象時不可能再被使用的。

客觀的來說,引用計數法實現簡單,判定效率高,但是無法解決對象的循環引用的問題。所以現在的虛擬機很少使用這種算法辣判斷對象是否存活。

可達性分析算法

基本思路就是:通過一系列稱為GC Roots的對象作為起始點,從這些起始點開始向下搜索,搜索所搜過的路徑稱為引用鏈Reference Chain,當一個對象到GC Roots沒有任何引用鏈相連接時,則證明此對象時不可用的。如下圖所示:4,5就被判定為需要回收的對象

GC Roots

在虛擬機中可作為GC Roots的對象包括以下幾種:

  • 虛擬機棧中引用的對象
  • 方法區中類靜態屬性引用的對象
  • 方法區常量引用的對象
  • 本地方法棧引用的對象

GC算法

在被可達性分析算法判定為需要被回收的對象時,需要進行垃圾回收操作,垃圾回收算法有以下幾種:

標記清除(mark-sweep)

最基礎的收集算法,如同 它的名字一樣,算法分為標記清除兩個階段:首先標記出需要被回收的對象,在標記完成后統一回收所有被標記的對象,它的標記過程其實就是上面所講的可達性分析算法被標記為沒有GC roots的引用鏈。過程如下:

不足
  • 效率問題,標記和清除兩個過程的效率都不高
  • 空間問題,標記清除和產生大量不連續的內存碎片,內存碎片過多容易導致以后在程序運行的過程中需要分配大對象時,無法找到足夠的連續內存而不得不提前觸發一次垃圾收集動作。
適用場景

適用於老年代。因為老年代回收的幾率小且不頻繁能減少內存碎片

復制 (coping)

為了解決標記-清除得到效率問題,復制算法就出現了;它的原理是將可用內存容量划分為兩個大小相等的內存塊,每次只使用其中的一個,當這個的容量用完了。就將還存活的對象復制到另外一塊上面去,然后再把已使用過的內存空間全部清理掉。這樣使得每次都是對整個半區進行內存回收,內存分配就不用考考慮碎片等復雜情況


研究表明,新生代中的對象98%都是朝生夕死的,所以按照並不需要1:1來划分空間。而是將內存分為一塊較大的Eden空間和兩個較小的Survivor空間,每次使用Eden和其中一款Survivor。一般比例是Eden : Survivor = 8:1

不足
  • 空間利用率只有50%,但是經過適當的調整Eden和Survivor的比例能達到70%
  • 復制收集算法在對象存活率較高時就要進行較多的復制操作,效率將會變低
適用場景

使用了對象存活率較低的內存空間,如Eden區。

標記整理

是標記-清除算法的升級版本。標記過程任然與標記-清除算法一樣,但是后續的處理步驟不是直接對可回收對象進行清理,而是讓所有存活對象都向一端移動,然后直接清理掉端邊界意外的內存、標記-整理算法示意圖如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM