JVM垃圾回收機制(Garbage Collection)

1. 概述

Java內(nèi)存區(qū)域里講了Java的內(nèi)存運行時數(shù)據(jù)區(qū)域分為如下5個部分

  • 程序計數(shù)器(Program Counter)
  • 虛擬機棧(Virtual Machine Stack)
  • 本地方法棧(Native Method Stack)
  • 堆(Heap)
  • 方法區(qū)(Method Area)

其中前三個數(shù)據(jù)區(qū)域隨著線程的啟動而創(chuàng)建涮总,終止而銷毀颜及,這三個區(qū)域的內(nèi)存回收具有確定性宝磨,不需要過多考慮回收問題。所以JVM的垃圾回收機制的注意力就集中于堆和方法區(qū)闸婴,其中對堆的GC性價比是最高的,一般可以回收70%~95%的空間馍管。

2. GC過程

首先討論的是對堆的GC船殉,在這之前我們應(yīng)該知道要進行垃圾回收的步驟應(yīng)該是

  • 知道哪些對象需要回收?
  • 用什么方式去回收炕桨?

判斷對象的存活

針對第一個問題我們得確定堆中對象的“存活”饭尝,一個對象的“存活”其實就是能否通過任何途徑使用該對象,下面通過一段Code看下就明白:

public class Main{
    public static void main(String[] args){
        A a = new A();
        a = null;
    }
}

在這段Code里面献宫,一開始創(chuàng)建一個A類型的對象钥平,變量a持有這個對象的引用,接著a被賦值為null后姊途。從此就無法通過任何變量來使用這個對象了涉瘾,那么這個對象也就是所謂的“死亡”了,而GC的 就是這些對象捷兰。接下來有兩種方法可以找出堆中存活和死亡的對象立叛。

引用計數(shù)法(Reference Counting)

給每一個對象添加一個引用計數(shù)器,每當(dāng)對象被引用贡茅,就對該對象的引用計數(shù)器加一秘蛇,當(dāng)引用失效時引用計數(shù)器就減一。直到對象的引用計數(shù)器為0時該對象就是已死亡友扰,可被GC彤叉。這種方法看起來簡單高效庶柿,但JVM卻沒有使用它來判斷對象的存活村怪,原因是它很難解決對象之間相互引用的問題。還是來一段Code看下:

public class Main{
    public static void main(String[] args){
        A a = new A();
        B b = new B();

        a.ref = b;
        b.ref = a;

        a = null;
        b = null;
    }
}

在這段Code中浮庐,ab兩個引用最后都null甚负,也就是無法通過它們來使用一開始創(chuàng)建的兩個對象,雖然這樣它們卻無法回收审残,原因是創(chuàng)建的兩個對象相互引用導(dǎo)致兩個對象的引用計數(shù)器都不為0梭域。所以也就有了第二種方法(可達性分析)來解決這個問題。

可達性分析算法(Reachability Analysis)

把堆中所有對象當(dāng)成一幅有向圖中的所有點搅轿,對象之間的引用構(gòu)成了點與點的之間的路徑病涨。接著從一系列被稱為GC Roots(一些被引用的對象)的點出發(fā)遍歷整個圖,圖中所有可以到達的點都是存活的對象璧坟,而那些不可到達的點則為死亡對象既穆,將被GC赎懦。
可充當(dāng)GC Roots的對象有下面幾種:

  • 虛擬機棧中棧幀中本地變量表中變量引用的對象
  • 本地方法棧中本地的方法引用的對象
  • 方法區(qū)中類靜態(tài)變量引用的對象
  • 方法區(qū)中常量引用的對象

垃圾回收算法

解決完第一個問題(判斷對象的存活)后,就可以去回收這些對象占用的內(nèi)存了幻工,至于怎么回收這些內(nèi)存励两,有下面幾種算法:

標(biāo)記-清除算法(Mark-Sweep)

標(biāo)記-清除算法如同它的名字一樣,有標(biāo)記和清除兩個階段囊颅。其中的標(biāo)記階段就是上面說到的確定對象的存活階段当悔,確定了要回收的對象后就回收死亡的對象,存活的對象留在原地踢代。標(biāo)記清除算法是最基礎(chǔ)的回收算法盲憎,它有兩個缺點:

  • 標(biāo)記和清除階段效率都不高
  • 清除之后內(nèi)存會產(chǎn)生大量不連續(xù)的碎片,導(dǎo)致分配大內(nèi)存對象困難


    標(biāo)記清除算法

復(fù)制算法(Copying)

復(fù)制算法將內(nèi)存分為大小相等的兩塊奸鬓,每次只使用一塊焙畔,待這塊內(nèi)存用完,將這一塊上存活的對象復(fù)制到另一塊上串远,再把存在垃圾對象的那一塊占用的內(nèi)存一次清掉宏多。這樣做效率高的原因是存活的對象遠(yuǎn)遠(yuǎn)少于死亡的對象,從而只需復(fù)制少量的存活對象澡罚。

復(fù)制算法

復(fù)制算法解決了標(biāo)記-清除算法的清除階段效率低的問題和碎片問題但卻使可用內(nèi)存減少一半伸但。其實有個辦法可以解決這個問題:

IBM公司的專業(yè)研究表明新生代中的對象98%是“朝生夕死”的,所以并不用按照1:1來劃分空間留搔,而是將內(nèi)存分為3塊更胖。一塊80%大小的Eden空間和兩塊10%大小的Survivor空間,每次使用一塊Eden和一塊Survivor隔显,當(dāng)需要回收時却妨,將使用中的Eden和Survivor上的存活對象復(fù)制到另一塊Survivor上,最后直接清理使用過的Eden和Survivor的內(nèi)存空間括眠。這樣就使得空間的利用率達到90%彪标。但如果存活的對象超過10%的話,Survivor的空間就不夠用了掷豺,這時就需要依賴?yán)夏甏M行分配擔(dān)保捞烟。

標(biāo)記整理算法(Mark-Compact)

相比于復(fù)制算法,標(biāo)記整理算法使用與適用于老年代這種對象存活率高的區(qū)域当船。標(biāo)記整理和標(biāo)記清除很相似题画,前面的標(biāo)記步驟都一樣,不一樣在標(biāo)記整理在清除前多做了整理步驟讓存活的對象向一端移動德频,最后在清除掉端邊界以外的內(nèi)存苍息。


標(biāo)記整理算法

分代收集算法(Generational Collection)

因為現(xiàn)在的商用JVM的垃圾回收都采用分代收集算法,所以一般把堆內(nèi)存劃分為新生代和老年代。剛創(chuàng)建的對象存在于新生代中竞思,當(dāng)有一些對象經(jīng)歷垃圾回收達到一定次數(shù)還存活下來的話桌粉,這些對象將進入老年代,所以老年代里的對象每次GC存活率都很高衙四。因此針對于新生代和老年代對象的不同存活率铃肯,可以分別采取不同的垃圾回收算法,對于對象存活率低的新生代采用復(fù)制算法传蹈,而對于對象存活率高的老年代采用標(biāo)記清除或標(biāo)記整理算法押逼。

以上介紹的是關(guān)于堆中的GC,下面來說下方法區(qū)的GC惦界。


方法區(qū)的GC

方法區(qū)在HotSpot虛擬機中是永久代挑格,相比于堆中的新生代和老年代,永久代進行垃圾回收的性價比更低沾歪。
方法區(qū)的垃圾回收主要回收廢棄常量和無用的類漂彤,其中常量來自于方法區(qū)的常量池,包括字面值常量和符號引用灾搏〈焱回收常量跟回收堆中對象非常類似,以字面值常量為例狂窑,如果不存在其他對象引用該字面值常量媳板,如果發(fā)生GC且有必要的話,該字面值常量會被回收泉哈。對于無用的類的判斷比較苛刻蛉幸,必須同時滿足下列三個條件:

  • 該類的所以實例都被回收
  • 加載該類的類加載器已經(jīng)被回收
  • 該類對應(yīng)的Class對象沒有在任何地方被引用

不過也可以滿足了上面的三個條件也不進行回收,可以通過設(shè)置虛擬機參數(shù)來控制回收丛晦。

3. 內(nèi)存分配策略

  1. 對象優(yōu)先在 Eden 分配
    對象優(yōu)先在新生代的 Eden 區(qū)分配奕纫,當(dāng) Eden 區(qū)空間不夠時,執(zhí)行Minor GC

  2. 大對象直接進入老年代
    設(shè)置 -XX:PretenureSizeThreshold 參數(shù)烫沙,大于該參數(shù)的值的對象直接在老年代分配匹层,避免在 Eden 區(qū)和 Survivor 區(qū)之間的大量內(nèi)存復(fù)制

  3. 長期存活的對象進入老年代
    對象頭的Mark word擁有一個存儲分代年齡字段,每經(jīng)歷一次 Minor GC 存活下來該年齡字段加1斧吐,直到該年齡超過 XX:MaxTenuringThreshold 設(shè)置的值(默認(rèn)15)又固,則移動到老年代仲器。

  4. 動態(tài)對象年齡判定
    若 Survivor 區(qū)中同年齡所有對象大小總和大于 Survivor 空間一半煤率,則年齡大于等于該年齡的對象可以直接進入老年代。

  5. 空間分配擔(dān)保
    在發(fā)生 Minor GC 之前乏冀,JVM 先檢查老年代最大可用連續(xù)空間是否大于新生代所有對象大小蝶糯,成立的話 Minor GC 確認(rèn)是安全的,則進行Minor GC辆沦;否則如果 HandlePromotionFailure 設(shè)置的值為true并且老年代最大可用連續(xù)空間大于歷次晉升到老年代對象的平均大小昼捍,則進行 Minor GC识虚,否則進行 Full GC。

4. Minor GC 與 Full GC

觸發(fā)條件

  • Minor GC:當(dāng) Eden 區(qū)空間滿時妒茬,就將觸發(fā) Minor GC
  • Full GC:
    • 調(diào)用 System.gc() 大多情況下回觸發(fā)Full GC担锤,通過 -XX:+ DisableExplicitGC 來禁止 RMI 調(diào)用System.gc()。
    • 老年代空間不足
    • 空間分配擔(dān)保失敗
    • JDK 1.7 及以前的永久代空間不足
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乍钻,一起剝皮案震驚了整個濱河市肛循,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌银择,老刑警劉巖多糠,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異浩考,居然都是意外死亡夹孔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門析孽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搭伤,“玉大人,你說我怎么就攤上這事袜瞬∶苹” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵吞滞,是天一觀的道長佑菩。 經(jīng)常有香客問我,道長裁赠,這世上最難降的妖魔是什么殿漠? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮佩捞,結(jié)果婚禮上绞幌,老公的妹妹穿的比我還像新娘。我一直安慰自己一忱,他們只是感情好莲蜘,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著帘营,像睡著了一般票渠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芬迄,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天问顷,我揣著相機與錄音,去河邊找鬼。 笑死杜窄,一個胖子當(dāng)著我的面吹牛肠骆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塞耕,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蚀腿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扫外?” 一聲冷哼從身側(cè)響起唯咬,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎畏浆,沒想到半個月后胆胰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡刻获,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年蜀涨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝎毡。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡厚柳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沐兵,到底是詐尸還是另有隱情别垮,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布扎谎,位于F島的核電站碳想,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏毁靶。R本人自食惡果不足惜胧奔,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望预吆。 院中可真熱鬧龙填,春花似錦、人聲如沸拐叉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凤瘦。三九已至宿礁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廷粒,已是汗流浹背窘拯。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坝茎,地道東北人涤姊。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像嗤放,于是被迫代替她去往敵國和親思喊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

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

  • 1.什么是垃圾回收次酌? 垃圾回收(Garbage Collection)是Java虛擬機(JVM)垃圾回收器提供...
    簡欲明心閱讀 89,484評論 17 311
  • JVM架構(gòu) 當(dāng)一個程序啟動之前恨课,它的class會被類裝載器裝入方法區(qū)(Permanent區(qū)),執(zhí)行引擎讀取方法區(qū)的...
    cocohaifang閱讀 1,664評論 0 7
  • JVM筆記 JDK:Java岳服、JVM剂公、Java API類庫,是支持java程序開發(fā)的最小環(huán)境吊宋。JRE:Java A...
    一條小袍袍YoY閱讀 875評論 0 4
  • JVM內(nèi)存區(qū)域 JVM將其管理的內(nèi)存分為若干數(shù)據(jù)區(qū)域纲辽,這些數(shù)據(jù)區(qū)域分布情況如下圖所示: 程序計數(shù)器:一塊較小內(nèi)存區(qū)...
    luoxn28閱讀 696評論 0 0
  • 【徐水聽課雜感之下篇 把學(xué)生的發(fā)現(xiàn)都成為我的課堂資源———原來繪本可以這樣讀】 從沒有人是在讀書,每個人都在書本中...
    在水一方2008閱讀 184評論 0 0