垃圾回收算法、回收機制與總結 ---JVM
一、垃圾回收算法
1.標記清除
標記--清除算法將垃圾回收分為兩個階段:標記階段和清除階段。
在標記階段首先通過根節點(GC Roots),標記所有從根節點開始的對象,未被標記的對象就是未被引用的垃圾對象。然后,在清除階段,清除所有未被標記的對象。如下圖
[圖片上傳失敗...(image-601690-1618061506012)]
適用場景:
- 對于存活對象較多的情況下比較高效
- 適用于年老代(即舊生代)
缺點:
- 容易產生內存碎片,再來一個比較大的對象時(典型情況:該對象的大小大于空閑表中的每一塊兒大小但是小于其中兩塊兒的和),會提前觸發垃圾回收
- 掃描了整個空間兩次(第一次:標記存活對象;第二次:清除沒有標記的對象)
2.復制算法
從根集合節點進行掃描,標記出所有的存活對象,并將這些存活的對象復制到一塊兒新的內存(圖中下邊的那一塊兒內存)上去,之后將原來的那一塊兒內存(圖中上邊的那一塊兒內存)全部回收掉
[圖片上傳失敗...(image-9bc14e-1618061506011)]
現在很多的的商業虛擬機都采用這種收集算法來回收新生代。
適用場合:
- 存活對象較少的情況下比較高效
- 掃描了整個空間一次(標記存活對象并復制移動)
- 適用于年輕代(即新生代):基本上98%的對象是"朝生夕死"的,存活下來的會很少
缺點:
- 需要一塊兒空的內存空間
- 需要復制移動對象
3.標記整理
復制算法的高效性是建立在存活對象少、垃圾對象多的前提下的。
這種情況在新生代經常發生,但是在老年代更常見的情況是大部分對象都是存活對象。如果依然使用復制算法,由于存活的對象較多,復制的成本也將很高。
[圖片上傳失敗...(image-ee4c8c-1618061506011)]
標記-壓縮算法是一種老年代的回收算法,它在標記-清除算法的基礎上做了一些優化。
首先也需要從根節點開始對所有可達對象做一次標記,但之后,它并不簡單地清理未標記的對象,而是將所有的存活對象壓縮到內存的一端。之后,清理邊界外所有的空間。這種方法既避免了碎片的產生,又不需要兩塊相同的內存空間,因此,其性價比比較高。
4.分代收集算法
分代收集算法就是目前虛擬機使用的回收算法,它解決了標記整理不適用于老年代的問題,將內存分為各個年代。一般情況下將堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)。
在不同年代使用不同的算法,從而使用最合適的算法,新生代存活率低,可以使用復制算法。而老年代對象存活率搞,沒有額外空間對它進行分配擔保,所以只能使用標記清除或者標記整理算法。
二、垃圾回收機制
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.年輕代:復制算法
所有新生成的對象首先都是放在年輕代的。年輕代的目標就是盡可能快速的收集掉那些生命周期短的對象。
新生代內存按照8:1:1的比例分為一個eden區和兩個survivor(survivor0,survivor1)區。一個Eden區,兩個 Survivor區(一般而言)。大部分對象在Eden區中生成。回收時先將eden區存活對象復制到一個survivor0區,然后清空eden區,當這個survivor0區也存放滿了時,則將eden區和survivor0區存活對象復制到另一個survivor1區,然后清空eden和這個survivor0區,此時survivor0區是空的,然后將survivor0區和survivor1區交換,即保持survivor1區為空, 如此往復。
當survivor1區不足以存放 eden和survivor0的存活對象時,就將存活對象直接存放到老年代。若是老年代也滿了就會觸發一次Full GC(Major GC),也就是新生代、老年代都進行回收。
新生代發生的GC也叫做Minor GC,MinorGC發生頻率比較高(不一定等Eden區滿了才觸發)。
2.年老代:標記-清除或標記-整理
在年輕代中經歷了N次垃圾回收后仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。
內存比新生代也大很多(大概比例是1:2),當老年代內存滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代對象存活時間比較長,存活率標記高。
以上這種年輕代與年老代分別采用不同回收算法的方式稱為"分代收集算法",這也是當下企業使用的一種方式
3)每一種算法都會有很多不同的垃圾回收器去實現,在實際使用中,根據自己的業務特點做出選擇就好。