Java GC的那些事(1)

簡書 占小狼
轉(zhuǎn)載請注明原創(chuàng)出處扎唾,謝謝营密!

前言

與C語言不同澄步,Java內(nèi)存(堆內(nèi)存)的分配與回收由JVM垃圾收集器自動完成冰蘑,這個特性深受大家歡迎,能夠幫助程序員更好的編寫代碼驮俗,本文以HotSpot虛擬機為例懂缕,說一說Java GC的那些事。

Java堆內(nèi)存

JVM內(nèi)存的那些事 一文中王凑,我們已經(jīng)知道Java堆是被所有線程共享的一塊內(nèi)存區(qū)域搪柑,所有對象實例和數(shù)組都在堆上進行內(nèi)存分配。為了進行高效的垃圾回收索烹,虛擬機把堆內(nèi)存劃分成新生代(Young Generation)工碾、老年代(Old Generation)和永久代(Permanent Generation)3個區(qū)域。

新生代

新生代由 Eden 與 Survivor Space(S0百姓,S1)構(gòu)成渊额,大小通過-Xmn參數(shù)指定,Eden 與 Survivor Space 的內(nèi)存大小比例默認為8:1,可以通過-XX:SurvivorRatio 參數(shù)指定旬迹,比如新生代為10M 時火惊,Eden分配8M,S0和S1各分配1M奔垦。

Eden:希臘語屹耐,意思為伊甸園,在圣經(jīng)中椿猎,伊甸園含有樂園的意思惶岭,根據(jù)《舊約·創(chuàng)世紀》記載,上帝耶和華照自己的形像造了第一個男人亞當(dāng)犯眠,再用亞當(dāng)?shù)囊粋€肋骨創(chuàng)造了一個女人夏娃按灶,并安置他們住在了伊甸園。

大多數(shù)情況下筐咧,對象在Eden中分配鸯旁,當(dāng)Eden沒有足夠空間時,會觸發(fā)一次Minor GC嗜浮,虛擬機提供了-XX:+PrintGCDetails參數(shù)羡亩,告訴虛擬機在發(fā)生垃圾回收時打印內(nèi)存回收日志。

Survivor:意思為幸存者危融,是新生代和老年代的緩沖區(qū)域畏铆。
當(dāng)新生代發(fā)生GC(Minor GC)時,會將存活的對象移動到S0內(nèi)存區(qū)域吉殃,并清空Eden區(qū)域辞居,當(dāng)再次發(fā)生Minor GC時,將Eden和S0中存活的對象移動到S1內(nèi)存區(qū)域蛋勺。

存活對象會反復(fù)在S0和S1之間移動瓦灶,當(dāng)對象從Eden移動到Survivor或者在Survivor之間移動時,對象的GC年齡自動累加抱完,當(dāng)GC年齡超過默認閾值15時贼陶,會將該對象移動到老年代,可以通過參數(shù)-XX:MaxTenuringThreshold 對GC年齡的閾值進行設(shè)置巧娱。

老年代

老年代的空間大小即-Xmx 與-Xmn 兩個參數(shù)之差碉怔,用于存放經(jīng)過幾次Minor GC之后依舊存活的對象。當(dāng)老年代的空間不足時禁添,會觸發(fā)Major GC/Full GC撮胧,速度一般比Minor GC慢10倍以上。

永久代

在JDK8之前的HotSpot實現(xiàn)中老翘,類的元數(shù)據(jù)如方法數(shù)據(jù)芹啥、方法信息(字節(jié)碼锻离,棧和變量大小)墓怀、運行時常量池汽纠、已確定的符號引用和虛方法表等被保存在永久代中,32位默認永久代的大小為64M傀履,64位默認為85M疏虫,可以通過參數(shù)-XX:MaxPermSize進行設(shè)置,一旦類的元數(shù)據(jù)超過了永久代大小啤呼,就會拋出OOM異常。

虛擬機團隊在JDK8的HotSpot中呢袱,把永久代從Java堆中移除了官扣,并把類的元數(shù)據(jù)直接保存在本地內(nèi)存區(qū)域(堆外內(nèi)存),稱之為元空間羞福。

這樣做有什么好處惕蹄?
有經(jīng)驗的同學(xué)會發(fā)現(xiàn),對永久代的調(diào)優(yōu)過程非常困難治专,永久代的大小很難確定卖陵,其中涉及到太多因素,如類的總數(shù)张峰、常量池大小和方法數(shù)量等泪蔫,而且永久代的數(shù)據(jù)可能會隨著每一次Full GC而發(fā)生移動。

而在JDK8中喘批,類的元數(shù)據(jù)保存在本地內(nèi)存中撩荣,元空間的最大可分配空間就是系統(tǒng)可用內(nèi)存空間,可以避免永久代的內(nèi)存溢出問題饶深,不過需要監(jiān)控內(nèi)存的消耗情況餐曹,一旦發(fā)生內(nèi)存泄漏,會占用大量的本地內(nèi)存敌厘。

ps:JDK7之前的HotSpot台猴,字符串常量池的字符串被存儲在永久代中,因此可能導(dǎo)致一系列的性能問題和內(nèi)存溢出錯誤俱两。在JDK8中饱狂,字符串常量池中只保存字符串的引用。

如何判斷對象是否存活

GC動作發(fā)生之前锋华,需要確定堆內(nèi)存中哪些對象是存活的嗡官,一般有兩種方法:引用計數(shù)法和可達性分析法。

1毯焕、引用計數(shù)法
在對象上添加一個引用計數(shù)器衍腥,每當(dāng)有一個對象引用它時磺樱,計數(shù)器加1,當(dāng)使用完該對象時婆咸,計數(shù)器減1竹捉,計數(shù)器值為0的對象表示不可能再被使用。

引用計數(shù)法實現(xiàn)簡單尚骄,判定高效块差,但不能解決對象之間相互引用的問題。

public class GCtest {
    private Object instance = null;
    private static final int _10M = 10 * 1 << 20;
    // 一個對象占10M倔丈,方便在GC日志中看出是否被回收
    private byte[] bigSize = new byte[_10M];

    public static void main(String[] args) {
        GCtest objA = new GCtest();
        GCtest objB = new GCtest();

        objA.instance = objB;
        objB.instance = objA;

        objA = null;
        objB = null;

        System.gc();
    }
}

通過添加-XX:+PrintGC參數(shù)憨闰,運行結(jié)果:

[GC (System.gc()) [PSYoungGen: 26982K->1194K(75776K)] 26982K->1202K(249344K), 0.0010103 secs]

從GC日志中可以看出objA和objB雖然相互引用,但是它們所占的內(nèi)存還是被垃圾收集器回收了需五。

2鹉动、可達性分析法
通過一系列稱為 “GC Roots” 的對象作為起點,從這些節(jié)點開始向下搜索宏邮,搜索路徑稱為 “引用鏈”泽示,以下對象可作為GC Roots:

  • 本地變量表中引用的對象
  • 方法區(qū)中靜態(tài)變量引用的對象
  • 方法區(qū)中常量引用的對象
  • Native方法引用的對象

當(dāng)一個對象到 GC Roots 沒有任何引用鏈時,意味著該對象可以被回收蜜氨。


在可達性分析法中械筛,判定一個對象objA是否可回收,至少要經(jīng)歷兩次標記過程:
1飒炎、如果對象objA到 GC Roots沒有引用鏈埋哟,則進行第一次標記。
2郎汪、如果對象objA重寫了finalize()方法定欧,且還未執(zhí)行過,那么objA會被插入到F-Queue隊列中怒竿,由一個虛擬機自動創(chuàng)建的砍鸠、低優(yōu)先級的Finalizer線程觸發(fā)其finalize()方法。finalize()方法是對象逃脫死亡的最后機會耕驰,GC會對隊列中的對象進行第二次標記爷辱,如果objA在finalize()方法中與引用鏈上的任何一個對象建立聯(lián)系,那么在第二次標記時朦肘,objA會被移出“即將回收”集合饭弓。

看看具體實現(xiàn)

public class FinalizerTest {
    public static FinalizerTest object;
    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 Exception {
        object = new FinalizerTest();

        // 第一次執(zhí)行,finalize方法會自救
        object = null;
        System.gc();

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

        // 第二次執(zhí)行媒抠,finalize方法已經(jīng)執(zhí)行過
        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

從執(zhí)行結(jié)果可以看出:
第一次發(fā)生GC時弟断,finalize方法的確執(zhí)行了,并且在被回收之前成功逃脫趴生;
第二次發(fā)生GC時阀趴,由于finalize方法只會被JVM調(diào)用一次昏翰,object被回收。

當(dāng)然了刘急,在實際項目中應(yīng)該盡量避免使用finalize方法棚菊。

END。
我是占小狼叔汁。
在魔都艱苦奮斗统求,白天是上班族,晚上是知識服務(wù)工作者据块。
如果讀完覺得有收獲的話码邻,記得關(guān)注和點贊哦。
非要打賞的話另假,我也是不會拒絕的冒滩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市浪谴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌因苹,老刑警劉巖苟耻,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扶檐,居然都是意外死亡凶杖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門款筑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來智蝠,“玉大人,你說我怎么就攤上這事奈梳¤就澹” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵攘须,是天一觀的道長漆撞。 經(jīng)常有香客問我,道長于宙,這世上最難降的妖魔是什么浮驳? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮捞魁,結(jié)果婚禮上至会,老公的妹妹穿的比我還像新娘。我一直安慰自己谱俭,他們只是感情好奉件,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布宵蛀。 她就那樣靜靜地躺著,像睡著了一般瓶蚂。 火紅的嫁衣襯著肌膚如雪糖埋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天窃这,我揣著相機與錄音瞳别,去河邊找鬼。 笑死杭攻,一個胖子當(dāng)著我的面吹牛祟敛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兆解,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼馆铁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锅睛?” 一聲冷哼從身側(cè)響起埠巨,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎现拒,沒想到半個月后辣垒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡印蔬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年勋桶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侥猬。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡例驹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出退唠,到底是詐尸還是另有隱情鹃锈,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布瞧预,位于F島的核電站仪召,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏松蒜。R本人自食惡果不足惜扔茅,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秸苗。 院中可真熱鬧召娜,春花似錦、人聲如沸惊楼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雅倒,卻和暖如春璃诀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔑匣。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工劣欢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裁良。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓凿将,卻偏偏與公主長得像,于是被迫代替她去往敵國和親价脾。 傳聞我的和親對象是個殘疾皇子牧抵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容