概述
下文主要分為以下幾個大模塊進行JVM的GC解讀:
- 垃圾回收之標記算法
- 垃圾回收之回收算法
- 堆內(nèi)存年輕代垃圾收集器
- 堆內(nèi)存老年代垃圾收集器
1.垃圾回收之標記算法
既然是垃圾回收苹熏,首先就是要判斷哪些對象實例是垃圾,可以被回收题篷,標記算法的用處就在于此
引用計數(shù)法
Java中通過引用關(guān)聯(lián)對象掏膏,顯然可以通過引用計數(shù)的方式來判斷一個對象是否可以被回收。如果一個對象沒有和任何一個引用相關(guān)聯(lián)钠署,那這個對象就可以被回收徘熔。這種算法實現(xiàn)方式簡單糖驴,效率高洪唐,但是無法解決循環(huán)引用的問題钻蹬,如下這段代碼:
public class Main {
public static void main(String[] args) {
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
object1.object = object2;
object2.object = object1;
object1 = null;
object2 = null;
}
}
class MyObject{
public Object object;
}
由于object1和object2相互引用對方,它們的引用計數(shù)永遠不為0凭需,永遠不會被標記而回收
可達性分析法
Java中采取的就是可達性分析算法问欠。此算法以GC Root為起點進
行引用鏈搜索,如果GC Root和目標對象之間沒有可達路徑粒蜈,則此對象不可達顺献,但是成為可被回收的對象需要經(jīng)歷兩次可達性分析算法的標記分析,如果兩次都被標記不可達才會被作為”垃圾“枯怖。
常見的GC Root有哪些呢注整?
- 虛擬機棧中的引用對象
- 方法區(qū)中的常量引用對象
- 方法區(qū)中靜態(tài)屬性引用對象
- 本地方法棧棧中JNI的引用對象
- 活躍線程的引用對象
2.垃圾回收之回收算法
標記算法完成了”垃圾“的標記,下一步就是回收這些”垃圾“
標記-清除算法(mark-sweep)
標記-清除算法分為兩步驟嫁怀,第一步就是利用標記算法找到”垃圾“设捐,第二步清除可被回收的對象借浊。此算法思路簡單易于實現(xiàn)塘淑,但是從上圖中可以看出標記-清除算法比較容易導(dǎo)致內(nèi)存碎片化,如果有較大的對象需要存儲時蚂斤,可能無法找到足夠的內(nèi)存空間存儲存捺,從而又觸發(fā)一次新的垃圾回收動作
復(fù)制算法(copying)
為了解決標記清除算法導(dǎo)致的內(nèi)存碎片化問題,就有了復(fù)制算法曙蒸。
它把內(nèi)存空間分為兩大塊捌治,一塊為對象面,一塊為空閑面纽窟;新創(chuàng)建的對象都在對象面上肖油,直到對象面內(nèi)存空間滿了,把對象面里不是垃圾的存活對象全部復(fù)制到空閑面臂港,然后把整個對象面內(nèi)存空間全部清除森枪,如下圖:
此算法雖然避免了內(nèi)存碎片化视搏,但是可使用的內(nèi)存空間卻減少了一半,并且此算法效率和存貨對象的數(shù)目有很大關(guān)系县袱,適用于存活對象數(shù)目較少的情況
標記-整理算法(mark-compact)
標記整理算法是標記清除算法的更近一步浑娜,在完成垃圾標記后,不是直接清除式散,而是把存活對象都移動至內(nèi)存一端筋遭,按照地址順序排列,最后把末端地址后的內(nèi)存空間全部清除暴拄,如下圖:
此算法避免了內(nèi)存碎片化漓滔,也不需要把內(nèi)存空間分為兩大塊,適用于存活對象較多的情況
分代收集算法(generational-collection)
此算法是目前大部分JVM采取的垃圾收集算法乖篷,效率最高次和。它可以理解為一套”組合拳“算法,JVM根據(jù)對象的生命周期將堆劃分為年輕代和老年代那伐,新生代的特點就是每次垃圾回收有較多的垃圾需要回收踏施,老年代的特點就是每次垃圾回收有較少的垃圾需要回收。
因此對于年輕代的垃圾回收一般采用復(fù)制算法罕邀,因為年輕代的存活對象較少畅形,反之老年代一般采用標記整理算法,因為老年代的存活對象較多诉探。
年輕代單獨的垃圾回收被稱為Minor GC日熬,年輕代的內(nèi)存劃分其實并不像復(fù)制算里面描述的分為1:1,而是分為一個較大的Eden區(qū)和兩個較小的Survivor區(qū)肾胯,大致空間大小劃分如下:
使用新代時竖席,只使用Eden區(qū)和其中一個Survivor區(qū),另一個Survivor區(qū)空閑敬肚。當進行Minor GC時把Eden區(qū)和Survivor區(qū)中的存活對象復(fù)制到另一個空閑的Survivor區(qū)毕荐,然后清除Eden區(qū)和使用過Survivor區(qū)。
整個堆的垃圾收集(包括年輕代和老年代)被稱為Full GC艳馒,也就是當觸發(fā)Full GC時同時也會觸發(fā)Minor GC憎亚,F(xiàn)ull GC的時間大概是Minor GC的十倍。觸發(fā)Full GC有以下幾個條件:
- 老年代空間不足
- 調(diào)用System.gc()
- 還有幾個我也不記得了
關(guān)于垃圾回收的幾個JVM性能調(diào)優(yōu)參數(shù)
- -XX:SurvivorRatio:新生代中Eden區(qū)Survivor區(qū)大小的比值弄慰,默認8:1
- -XX:NewRatio:老年代和年輕代的大小比值
- -XX:MaxTenuringThreshold:對象從年輕代晉升到老年代需要經(jīng)歷的GC次數(shù)的最大閾值
3.年輕代垃圾收集器
Serial收集器
使用-XX:+UseSerialGC設(shè)置年輕代使用此垃圾收集器第美,采用復(fù)制算法。單線程收集陆爽,在進行垃圾收集時必須暫停其他所有工作的線程什往,簡單高效,JVM的Client模式下的默認垃圾收集器
ParNew收集器
使用-XX:+UseParNewGC設(shè)置年輕代使用此垃圾收集器慌闭,采用復(fù)制算法别威。多線程收集第献,進行垃圾收集時和Serial一樣暫停其他工作線程,其在多核cpu的情況下才能發(fā)揮其優(yōu)勢
Parallel Scavenge收集器
使用-XX:+UseParallelGC設(shè)置年輕代使用此垃圾收集器兔港,采用復(fù)制算法庸毫。與ParNew一樣多線程收集,相比于前兩者收集器關(guān)注點為垃圾收集停頓時間衫樊,此垃圾收集器的關(guān)注點為吞吐量(用戶運行代碼的時間/用戶運行代碼的時間+垃圾收集時間),多核cpu才能發(fā)揮優(yōu)勢飒赃,JVM的Server模式下的默認垃圾收集器
如果對其垃圾收集器的工作原理不太理解,常常使用此垃圾收集器配合-XX:+UseAdaptiveSizePolicy自適應(yīng)調(diào)節(jié)策略科侈,把內(nèi)存調(diào)優(yōu)任務(wù)交給JVM
4.老年代垃圾收集器
Serial Old收集器
使用-XX:+UseSerialOldGC設(shè)置老年代使用此垃圾收集器载佳,采用標記-整理算法。其特點與Serial收集器的特點相同
Parallel Old收集器
使用-XX:+UseParallelGC設(shè)置老年代使用此垃圾收集器臀栈,采用標記-整理算法蔫慧。其特點與Parallel Scavenge相同,多線程收集权薯,吞吐量優(yōu)先姑躲,常與Parallel Scavenge配合使用達到高吞吐量的效果
CMS收集器
使用-XX:+UseConcMarkSweepGC設(shè)置老年代使用此垃圾收集器,采用標記-清除算法盟蚣。CMS是一種以獲取最短停頓時間為目的的收集器黍析,是垃圾收集和工作線程幾乎可以并行工作的收集器(并發(fā)收集器)
G1收集器
使用-XX:+UseG1GC設(shè)置老年代使用此垃圾收集器,采用復(fù)制+標記-整理算法屎开。它是一款并行并發(fā)收集器阐枣,對整個堆空間進行垃圾收集,能建立可預(yù)測的停頓時間模型
各個收集器之間的可兼容關(guān)系
圖中上半部分為年輕代垃圾收集器奄抽,下半部分為老年代垃圾收集器蔼两,連線意味著可兼容