1. 程式人生 > >JVM的4種垃圾回收演算法、垃圾回收機制與總結

JVM的4種垃圾回收演算法、垃圾回收機制與總結

本文標題:直通BAT必考題系列:JVM的4種垃圾回收演算法、垃圾回收機制與總結 
轉載請保留頁面地址:http://youzhixueyuan.com/jvm-garbage-collection-algorithm.html

ç´éBATå¿èé¢ç³»åï¼JVMç4ç§åå¾åæ¶ç®æ³ãåå¾åæ¶æºå¶ä¸æ»ç»

垃圾回收演算法

1.標記清除

標記-清除演算法將垃圾回收分為兩個階段:標記階段和清除階段

在標記階段首先通過根節點(GC Roots),標記所有從根節點開始的物件,未被標記的物件就是未被引用的垃圾物件。然後,在清除階段,清除所有未被標記的物件。

直通BAT必考題系列:JVM的4種垃圾回收演算法、垃圾回收機制與總結

適用場合

  •  存活物件較多的情況下比較高效
  •  適用於年老代(即舊生代)

缺點

  •  容易產生記憶體碎片,再來一個比較大的物件時(典型情況:該物件的大小大於空閒表中的每一塊兒大小但是小於其中兩塊兒的和),會提前觸發垃圾回收
  •  掃描了整個空間兩次(第一次:標記存活物件;第二次:清除沒有標記的物件)

2.複製演算法

從根集合節點進行掃描,標記出所有的存活物件,並將這些存活的物件複製到一塊兒新的記憶體(圖中下邊的那一塊兒記憶體)上去,之後將原來的那一塊兒記憶體(圖中上邊的那一塊兒記憶體)全部回收掉

直通BAT必考題系列:JVM的4種垃圾回收演算法、垃圾回收機制與總結

現在的商業虛擬機器都採用這種收集演算法來回收新生代。

適用場合:

  •  存活物件較少的情況下比較高效
  •  掃描了整個空間一次(標記存活物件並複製移動)
  •  適用於年輕代(即新生代):基本上98%的物件是”朝生夕死”的,存活下來的會很少

缺點:

  •  需要一塊兒空的記憶體空間
  •  需要複製移動物件

3.標記整理

複製演算法的高效性是建立在存活物件少、垃圾物件多的前提下的。

這種情況在新生代經常發生,但是在老年代更常見的情況是大部分物件都是存活物件。如果依然使用複製演算法,由於存活的物件較多,複製的成本也將很高。

直通BAT必考題系列:JVM的4種垃圾回收演算法、垃圾回收機制與總結

標記-壓縮演算法是一種老年代的回收演算法,它在標記-清除演算法的基礎上做了一些優化。

首先也需要從根節點開始對所有可達物件做一次標記,但之後,它並不簡單地清理未標記的物件,而是將所有的存活物件壓縮到記憶體的一端。之後,清理邊界外所有的空間。這種方法既避免了碎片的產生,又不需要兩塊相同的記憶體空間,因此,其價效比比較高。

4.分代收集演算法

分代收集演算法就是目前虛擬機器使用的回收演算法,它解決了標記整理不適用於老年代的問題,將記憶體分為各個年代。一般情況下將堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)。

在不同年代使用不同的演算法,從而使用最合適的演算法,新生代存活率低,可以使用複製演算法。而老年代物件存活率搞,沒有額外空間對它進行分配擔保,所以只能使用標記清除或者標記整理演算法。

直通BAT必考題系列:JVM的4種垃圾回收演算法、垃圾回收機制與總結

 

 

垃圾回收機制

年輕代分為Eden區和survivor區(兩塊兒:from和to),且Eden:from:to==8:1:1。

直通BAT必考題系列:JVM的4種垃圾回收演算法、垃圾回收機制與總結

 

jvm記憶體結構

1)新產生的物件優先分配在Eden區(除非配置了-XX:PretenureSizeThreshold,大於該值的物件會直接進入年老代);

2)當Eden區滿了或放不下了,這時候其中存活的物件會複製到from區。

這裡,需要注意的是,如果存活下來的物件from區都放不下,則這些存活下來的物件全部進入年老代。之後Eden區的記憶體全部回收掉。

3)之後產生的物件繼續分配在Eden區,當Eden區又滿了或放不下了,這時候將會把Eden區和from區存活下來的物件複製到to區(同理,如果存活下來的物件to區都放不下,則這些存活下來的物件全部進入年老代),之後回收掉Eden區和from區的所有記憶體。

4)如上這樣,會有很多物件會被複制很多次(每複製一次,物件的年齡就+1),預設情況下,當物件被複制了15次(這個次數可以通過:-XX:MaxTenuringThreshold來配置),就會進入年老代了。

5)當年老代滿了或者存放不下將要進入年老代的存活物件的時候,就會發生一次Full GC(這個是我們最需要減少的,因為耗時很嚴重)。

垃圾回收有兩種型別:Minor GC 和 Full GC。

1.Minor GC

對新生代進行回收,不會影響到年老代。因為新生代的 Java 物件大多死亡頻繁,所以 Minor GC 非常頻繁,一般在這裡使用速度快、效率高的演算法,使垃圾回收能儘快完成。

2.Full GC

也叫
Major GC,對整個堆進行回收,包括新生代和老年代。由於Full GC需要對整個堆進行回收,所以比Minor
GC要慢,因此應該儘可能減少Full GC的次數,導致Full
GC的原因包括:老年代被寫滿、永久代(Perm)被寫滿和System.gc()被顯式呼叫等。

垃圾回收演算法總結

1.年輕代:複製演算法

1) 所有新生成的物件首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的物件。

2)
新生代記憶體按照8:1:1的比例分為一個eden區和兩個survivor(survivor0,survivor1)區。一個Eden區,兩個
Survivor區(一般而言)。大部分物件在Eden區中生成。回收時先將eden區存活物件複製到一個survivor0區,然後清空eden區,當這個survivor0區也存放滿了時,則將eden區和survivor0區存活物件複製到另一個survivor1區,然後清空eden和這個survivor0區,此時survivor0區是空的,然後將survivor0區和survivor1區交換,即保持survivor1區為空,
如此往復。

3) 當survivor1區不足以存放 eden和survivor0的存活物件時,就將存活物件直接存放到老年代。若是老年代也滿了就會觸發一次Full GC(Major GC),也就是新生代、老年代都進行回收。

4) 新生代發生的GC也叫做Minor GC,MinorGC發生頻率比較高(不一定等Eden區滿了才觸發)。

2.年老代:標記-清除或標記-整理

1) 在年輕代中經歷了N次垃圾回收後仍然存活的物件,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命週期較長的物件。

2) 記憶體比新生代也大很多(大概比例是1:2),當老年代記憶體滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代物件存活時間比較長,存活率標記高。

以上這種年輕代與年老代分別採用不同回收演算法的方式稱為”分代收集演算法”,這也是當下企業使用的一種方式

3. 每一種演算法都會有很多不同的垃圾回收器去實現,在實際使用中,根據自己的業務特點做出選擇就好。