GC淺談(三)

人生并不像火車(chē)要通過(guò)每個(gè)站似的經(jīng)過(guò)每一個(gè)生活階段。人生總是直向前行走,從不留下什么。 —— 劉易斯

GC日志理解

每一種收集器的日志格式都可以不一樣的。 以下是兩段典型的GC日志:

0.173: [GC 0.173: [DefNew: 1353K->582K(19008K), 0.0015600 secs]0.175: [Tenured: 0K->581K(42368K), 0.0027216 secs] 1353K->581K(61376K), [Metaspace: 2551K->2551K(1056768K)], 0.0043860 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.275: [Full GC (System.gc()) 0.275: [Tenured: 205381K->205382K(247172K), 0.0035190 secs] 205722K->205382K(266308K), [Metaspace: 2551K->2551K(1056768K)], 0.0041874 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

最前面的數(shù)字"0.173"和"0.275"代表了GC發(fā)生的時(shí)間,這個(gè)數(shù)字的含義是從JVM啟動(dòng)以來(lái)經(jīng)過(guò)的秒數(shù)。
??GC日志開(kāi)頭的“[GC”和“[Full GC”說(shuō)明了這次垃圾收集的停頓類(lèi)型。如果有Full,說(shuō)明這次GC是發(fā)生了Stop the world的。如果是調(diào)用System.gc(),則是顯示“[Full GC (System.gc())”。
??“[DefNew”、“[Tenured”、“Perm”(JDK1.7及以下)、“[Metaspace”(jdk1.8)表示發(fā)生GC的區(qū)域。上面示例中使用的Serial收集器中的新生代名為“Default New Generation”,所以顯示“[DefNew”。如果是ParNew收集器,新生代名稱(chēng)就會(huì)變?yōu)椤癙arNew”,意為“Parallel New Genaration”。如果采用Parallel Scavenge收集器,那新生代名稱(chēng)就會(huì)變?yōu)椤癙SYoungGen”。老年代和永久代同理。
??“1353K->582K(19008K)”表示“GC前該內(nèi)存區(qū)域已使用容量->GC后該內(nèi)存區(qū)域已使用容量(該內(nèi)存區(qū)域總?cè)萘?”,方括號(hào)外部的“1353K->581K(61376K)”表示“GC前JAVA堆已使用容量->GC后JAVA堆已使用容量(JAVA堆總?cè)萘?”。
??“0.0015600 secs”表示該內(nèi)存區(qū)域GC所占用的時(shí)間,單位是秒?!癧Times: user=0.00 sys=0.00, real=0.00 secs]”分別代表用戶態(tài)消耗的CPU時(shí)間、內(nèi)核態(tài)消耗的CPU時(shí)間和操作從開(kāi)始到結(jié)束的墻鐘時(shí)間。CPU時(shí)間和墻鐘時(shí)間的區(qū)別是墻鐘時(shí)間包括各種非運(yùn)算的等待耗時(shí),如等待磁盤(pán)IO、等待線程阻塞等,而CPU時(shí)間不包括這些耗時(shí)。

JVM的GC日志的主要參數(shù)

-XX:+PrintGC 輸出GC日志
-XX:+PrintGCDetails 輸出GC的詳細(xì)日志
-XX:+PrintGCTimeStamps 輸出GC的時(shí)間戳(以基準(zhǔn)時(shí)間的形式)
-XX:+PrintGCDateStamps 輸出GC的時(shí)間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在進(jìn)行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的輸出路徑

GC類(lèi)型

在HotSpot中,GC分為兩大種類(lèi):

  • Partial GC: 不收集整個(gè)堆得GC
    ??Young GC: 只收集young gen的GC。
    ??Old GC:只收集old gen的GC。只有CMS的concurrent collection是這個(gè)模式。
    ??Mixed GC:收集young gen和部分old gen的GC,只有G1有這個(gè)模式。
  • Full GC:收集整個(gè)堆,包括young gen、old gen、perm gen(如果存在的話)等區(qū)域。

PS: Major GC通常是跟full GC是等價(jià)的,收集整個(gè)GC堆。

GC的觸發(fā)條件

  • Young GC:當(dāng)young gen中的eden區(qū)分配滿的時(shí)候觸發(fā)。
  • Full GC
    ??① 當(dāng)準(zhǔn)備要觸發(fā)一次young GC時(shí),如果發(fā)現(xiàn)統(tǒng)計(jì)數(shù)據(jù)說(shuō)之前young GC的平均晉升大小比目前old gen剩余的空間大,則不會(huì)執(zhí)行Young GC而是轉(zhuǎn)為執(zhí)行Full GC(因?yàn)镠otSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都會(huì)同時(shí)收集整個(gè)GC堆,包括young gen,所以不需要事先觸發(fā)一次單獨(dú)的young GC)。
    ??② 如果有perm gen的話,在perm gen上分配空間且沒(méi)有足夠空間時(shí),也要觸發(fā)Full GC。
    ??③ System.gc(),顯示的調(diào)用GC,也會(huì)觸發(fā)Full GC。
    ??④ Old gen空間不足:當(dāng)創(chuàng)建一個(gè)大對(duì)象、大數(shù)組時(shí),eden 區(qū)不足以分配這么大的空間,會(huì)嘗試在old gen 中分配,如果這時(shí) old gen 空間也不足時(shí),會(huì)觸發(fā) full gc。
    ??⑤ ygc出現(xiàn) promotion failure(晉升失?。簆romotion failure 發(fā)生在 young gc 階段,即 cms 的 ParNewGC,當(dāng)對(duì)象的gc年齡達(dá)到閾值時(shí),或者 eden 的 to 區(qū)放不下時(shí),會(huì)把該對(duì)象復(fù)制到 old gen,如果 old gen 空間不足時(shí),會(huì)發(fā)生 promotion failure,并接下去觸發(fā)full gc。

finalize詳解

對(duì)象object重寫(xiě)了finalize()方法,且還未執(zhí)行過(guò),那么object會(huì)被插入到F-Queue隊(duì)列中,由一個(gè)虛擬機(jī)自動(dòng)創(chuàng)建的、低優(yōu)先級(jí)的Finalizer線程觸發(fā)其finalize()方法。finalize()方法是對(duì)象逃脫死亡的最后機(jī)會(huì),GC會(huì)對(duì)隊(duì)列中的對(duì)象進(jìn)行第二次標(biāo)記。如果object在finalize()方法中與引用鏈上的任何一個(gè)對(duì)象建立聯(lián)系,那么在第二次標(biāo)記時(shí),object會(huì)被移出“即將回收”集合.
??finalize只會(huì)被執(zhí)行一次,下面通過(guò)例子來(lái)說(shuō)明下finalize。

public class FinalizeTest {

    public static FinalizeTest object;
    byte[] _200M = new byte[200 * 1024 * 1024];

    public void isAlive() {
        System.out.println("I'm alive");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("method finalize is running");
        object = this; 
    }

    public static void main(String[] args) throws InterruptedException {
        object = new FinalizeTest();

        object = null;
        System.gc();

        Thread.sleep(500);
        if (object != null) {
            object.isAlive();
        } else {
            System.out.println("I'm dead");
        }
        object = null;
        System.gc();

        Thread.sleep(500);
        if (object != null) {
            object.isAlive();
        } else {
            System.out.println("I'm dead");
        }
    }
}

執(zhí)行程序后,程序輸出結(jié)果:

method finalize is running
I'm alive
I'm dead

第一次GC時(shí),因在finalize方法中,將當(dāng)前對(duì)象賦值給了object,因此第一次未被回收。而第二次GC時(shí),由于finalize方法已經(jīng)執(zhí)行過(guò)了,因finalize方法只會(huì)被JVM調(diào)用一次,所以第二次GC時(shí),object被回收了。

參考資料
Major GC和Full GC的區(qū)別是什么?觸發(fā)條件呢?
雜談GC
《深入理解JAVA虛擬機(jī)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文閱讀 前言 這段時(shí)間懈怠了,罪過(guò)! 最近看到有同事也開(kāi)始用上了微信公眾號(hào)寫(xiě)博客了,挺好的~給他們點(diǎn)贊,這博客我...
    碼農(nóng)戲碼閱讀 6,157評(píng)論 2 31
  • 作者:一字馬胡 轉(zhuǎn)載標(biāo)志 【2017-11-12】 更新日志 日期更新內(nèi)容備注 2017-11-12新建文章初版 ...
    beneke閱讀 2,330評(píng)論 0 7
  • 簡(jiǎn)書(shū) 占小狼轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝 上周有幸給部門(mén)的小伙伴分享了一些JVM相關(guān)的知識(shí),在整個(gè)做PPT的過(guò)程中,也...
    美團(tuán)Java閱讀 8,407評(píng)論 19 76
  • GC整理 GC分類(lèi)在Hotspot VM實(shí)現(xiàn)中,主要有兩大類(lèi)GCPartial GCyoung gc:只收集 yo...
    andersonoy閱讀 455評(píng)論 0 1
  • 如果,有一種治療方法,我愿傾盡所有,我愿銼皮削骨,去彌補(bǔ)你們所認(rèn)為我犯下的錯(cuò)。 身為同性戀,我并不知道為何如此,是...
    嘿孤獨(dú)患者閱讀 441評(píng)論 0 1

友情鏈接更多精彩內(nèi)容