深入理解Java虛擬機(二):垃圾收集器與內(nèi)存分配策略

參考博客:https://www.cnblogs.com/parryyang/p/5748711.html

參考博客: https://blog.csdn.net/dongyuxu342719/article/details/78835431

概述

1睦尽、內(nèi)存回收的區(qū)域主要在堆和方法區(qū)廉羔,虛擬機棧和本地方法棧的內(nèi)存分配與回收具有確定性谢揪。

在Java內(nèi)存運行時區(qū)域的各個部分中,程序計數(shù)器、虛擬機棧卫病、本地方法棧3個部分是線程私有的都毒,隨線程而生臂外,隨線程而滅粤蝎;棧中的棧幀隨著方法進(jìn)入和退出而執(zhí)行著入棧和出棧的操作真仲。每一個棧幀中分配多少內(nèi)存基本上在類結(jié)構(gòu)確定下來時就已知,因此這幾個區(qū)域的內(nèi)存分配和回收都具備確定性初澎,在這幾個區(qū)域內(nèi)就不需要過多的考慮回收的問題秸应,因為方法結(jié)束或線程結(jié)束時,內(nèi)存自然就隨著回收了碑宴。

而在Java堆和方法區(qū)則不一樣软啼,一個接口中的多個實現(xiàn)類需要的內(nèi)存可能不一樣,一個方法中的多個分支需要的內(nèi)存可能不一樣延柠,我們只有在程序運行期間才能知道會創(chuàng)建哪些對象祸挪,這部分內(nèi)存的分配和回收都是動態(tài)的,垃圾收集器所關(guān)注的是這部分內(nèi)存贞间。

2贿条、如何判斷哪些對象需要回收?

接下來介紹的引用計數(shù)法和可達(dá)性分析法榜跌,引用計數(shù)法具有局限性闪唆,不能回收循環(huán)引用的對象。

3钓葫、如何回收對象悄蕾?

本章介紹的垃圾收集算法回答了這個問題。現(xiàn)在商用的垃圾回收機制一般使用分代收集算法础浮,年輕代使用復(fù)制算法帆调,老年代使用標(biāo)記整理法。

一豆同、對象已死嗎

1番刊、引用計數(shù)法

引用計數(shù)法是指給對象添加一個引用計數(shù)器,每當(dāng)有一個地方引用它時影锈,計數(shù)器值就加1芹务;當(dāng)引用失效時,計數(shù)器值就減1鸭廷;任何時候計數(shù)器值為0就表示不會再被任何對象使用枣抱。

客觀的說,引用計數(shù)法(Reference Counting)的實現(xiàn)簡單辆床,判斷效率也很高佳晶,在大部分情況下都是一個不錯的算法。但在主流的Java虛擬機里面沒有使用引用計數(shù)法來管理內(nèi)存讼载,主要原因是它很難解決對象之間相互循環(huán)引用的問題轿秧。

引用計數(shù)無法解決下面兩個對象相互引用但不可達(dá)的問題中跌,但運行代碼后發(fā)現(xiàn)對象實際上能夠被GC。

public class ReferenceCountingGC {  
2.    public Object instance=null;  
3.    private static final int _1MB=1024*1024;  
4.    private byte [] bigSize=new byte[2*_1MB];  
5.      
6.    public static void testGC(){  
7.        ReferenceCountingGC objA=new ReferenceCountingGC();  
8.        ReferenceCountingGC objB=new ReferenceCountingGC();  
9.        objA.instance=objB;  
10.        objB.instance=objA;  
11.        objA=null;  
12.        objB=null;  
13.        System.gc();  
14.    }  
15.    public static void main(String []args){  
16.        testGC();  
17.    }  
18.}  

2菇篡、可達(dá)性分析算法

在主流的商用程序語言(Java漩符、C#)的主流實現(xiàn)中,都是通過可達(dá)性分析(ReachabilityAnalysis)來判斷對象是否存活的驱还。這個算法的基本思路是通過一系列稱為GC Roots的對象作為起始點陨仅,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain)铝侵,當(dāng)一個對象到GC Roots沒有任何引用鏈(就是從GC Roots到這個對象不可達(dá))時,則證明此對象是不可用的触徐。

在Java語言中咪鲜,可作為GC Roots的對象包括下面幾種:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象。

  • 方法區(qū)中類靜態(tài)屬性引用的對象撞鹉。

  • 方法區(qū)中常量引用的對象疟丙。

  • 本地方法棧中JNI(即Native方法)引用的對象。

image

二鸟雏、垃圾收集算法

1享郊、標(biāo)記-清除法

分為“標(biāo)記”和“清除”兩個階段:首先標(biāo)記出需要回收的所有對象,在標(biāo)記完成后統(tǒng)一回收標(biāo)記的對象孝鹊,標(biāo)記過程就是之前講的通過引用計數(shù)法和可達(dá)性分析法進(jìn)行判定炊琉。

它的主要不足有兩個:

  • 效率問題:標(biāo)記和清除的效率都不高,

  • 空間問題:標(biāo)記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片又活,空間碎片太多可能會導(dǎo)致在需要分配較大對象時苔咪,無法找到連續(xù)的內(nèi)存空間而不得不提前觸發(fā)另一次垃圾收集動作。

image

2柳骄、復(fù)制算法

將內(nèi)存劃分為大小相等的兩塊团赏,每次只使用其中的一塊。當(dāng)這塊內(nèi)存用完了耐薯,就將還存活的對象復(fù)制到另一塊內(nèi)存上舔清,然后把已使用過的內(nèi)存空間一次清理掉。

優(yōu)點:每次只對其中一塊進(jìn)行GC,不用考慮內(nèi)存碎片的問題曲初,并且實現(xiàn)簡單体谒,運行高效

缺點:內(nèi)存縮小了一半

image

注:現(xiàn)在商用虛擬機都采用這種算法來回收新生代,因為新生代中98%的對象都是朝生夕死的复斥,所以并不需要1:1的比例來劃分內(nèi)存空間营密,而是將內(nèi)存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和一塊Survivor空間目锭。當(dāng)回收時评汰,將Eden和Survivor中開存活的對象一次性復(fù)制到另一塊Survivor空間上纷捞,最后清理掉Eden和剛才使用過的Survivor空間。

HotSpot虛擬機默認(rèn)Eden和Survivor的大小是8:1被去,也就是每次新生代可用空間是整個新生代容量的90%(80%+10%)主儡,只有10%的內(nèi)存會被“浪費”。當(dāng)然惨缆,98%可回收只是一般的情況的數(shù)據(jù)糜值,我們沒有辦法保證每次回收都只有不多于10%的對象存活,當(dāng)Survivor空間不夠用時坯墨,需要依賴其他內(nèi)存(這里指老年代)進(jìn)行分配擔(dān)保(Handle Promotion)寂汇。

內(nèi)存的分配擔(dān)保類似于銀行貸款,如果我們的信譽好捣染。在98%的情況下都能按時償還骄瓣,于是銀行可能默認(rèn)我們下次也能按時償還貸款,只需要有一個擔(dān)保人能保證如果我不能還款時耍攘,可以從他的賬戶扣錢榕栏,那銀行就認(rèn)為沒有風(fēng)險了。內(nèi)存的分配擔(dān)保也一樣蕾各,如果另外一塊Survivor空間沒有足夠空間存放上一次新生代收集下來的存活對象時扒磁,這些對象將直接通過分配擔(dān)保機制進(jìn)入老年代。

3式曲、標(biāo)記-整理法

復(fù)制收集算法在對象存活率較高時就要進(jìn)行較多的復(fù)制操作妨托,效率將會變低。更關(guān)鍵的是检访,如果不想浪費另外50%的空間始鱼,就需要額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對被使用的內(nèi)存中所有對象都100%存活的極端情況脆贵,所以老年代一般不能直接選用這種算法医清。

根據(jù)老年代的特點,有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法卖氨,標(biāo)記過程任然與“標(biāo)記-清除”算法的標(biāo)記過程相同会烙,但后續(xù)步驟不是對可回收對象進(jìn)行直接清理,而是讓所有活的對象都移動到另一端筒捺,然后直接清理掉端邊界以外的內(nèi)存柏腻,

image

4、分代收集算法

當(dāng)前商業(yè)虛擬機的垃圾收集都采用“分代收集”(GenerationalCollection)算法系吭,這種算法只是根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊五嫂。一般是把Java堆劃分為新生代和老年代,這樣就可以根據(jù)各個年代的特點采用最適合的收集算法。在新生代中每次都有大量的對象死去沃缘,只有少量存活躯枢,那就采用復(fù)制算法,只需要付出少量存活對象的復(fù)制成本就可以完成收集槐臀。而老年代中因為對象存活率高锄蹂、沒有額外空間對它們進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清理”或“標(biāo)記-整理”算法來進(jìn)行回收水慨。

5得糜、HotPot 分代收集算法

image

對象將根據(jù)存活的時間被分為:年輕代、年老代晰洒、永久代朝抖。

年輕代:

所有新生成的對象首先都是放在年輕代的。年輕代的目標(biāo)就是盡可能快速的收集掉那些生命周期短的對象谍珊。年輕代分三個區(qū)槽棍。一個Eden區(qū),兩個Survivor區(qū)(一般而言)抬驴。大部分對象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時缆巧,還存活的對象將被復(fù)制到Survivor區(qū)(兩個中的一個)布持,當(dāng)這個Survivor區(qū)滿時,此區(qū)的存活對象將被復(fù)制到另外一個Survivor區(qū)陕悬。同時题暖,根據(jù)程序需要,Survivor區(qū)是可以配置為多個的(多于兩個)捉超,這樣可以增加對象在年輕代中的存在時間胧卤,減少被放到年老代的可能。

年老代:

在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對象拼岳,就會被放到年老代中枝誊。因此,可以認(rèn)為年老代中存放的都是一些生命周期較長的對象惜纸。

持久代:

用于存放靜態(tài)文件叶撒,如Java類、方法等耐版。

Scavenge GC

一般情況下祠够,Eden空間滿時,就會觸發(fā)Scavenge GC粪牲,清除Eden區(qū)非存活對象古瓤,并且把尚且存活的對象移動到Survivor區(qū)。然后整理Survivor的兩個區(qū)。Eden區(qū)的GC會頻繁進(jìn)行落君,速度也很快穿香。

Full GC

對整個堆進(jìn)行整理,包括Young叽奥、Tenured和Perm扔水。Full GC因為需要對整個對進(jìn)行回收,所以比Scavenge GC要慢朝氓,因此應(yīng)該盡可能減少Full GC的次數(shù)歇攻。在對JVM調(diào)優(yōu)的過程中,很大一部分工作就是對于FullGC的調(diào)節(jié)另锋。

三俊柔、安全點:Stop the world

1、Stop the world

GC 操作的某些階段枫夺,如可達(dá)性分析遍歷 GC ROOT 節(jié)點找引用鏈将宪,需要確保在一致性的快照中進(jìn)行(一致性的意思指分析期間整個執(zhí)行系統(tǒng)好像凍結(jié)在某個時間節(jié)點上,不可以出現(xiàn)分析過程中對象引用關(guān)系還在不斷變化的情況橡庞,該點不滿足分析的準(zhǔn)確性就無法保證)较坛。這是導(dǎo)致GC進(jìn)行時必須停頓所有的執(zhí)行線程的其中一個重要原因。(Sun 將這件事情稱之為 “Stop the world”扒最。)即使是在號稱幾乎不會發(fā)生停頓的CMS收集器中丑勤,枚舉根節(jié)點時也是必須停頓的。

2吧趣、OopMap

OopMap:在所有執(zhí)行線程停頓下來后法竞,虛擬機并不需要逐個檢查每個棧的局部變量表來查找引用的位置,而應(yīng)當(dāng)有辦法直接得知哪些地方存著對象的應(yīng)用强挫。在執(zhí)行到某條指令時岔霸,棧中什么位置存放了什么變量是確定的,在編譯期間虛擬機就可以計算出這些信息俯渤。在 HotSpot 的實現(xiàn)中呆细,用一組稱為 OopMap 的數(shù)據(jù)結(jié)構(gòu)記錄了某一些執(zhí)行節(jié)點哪些位置是引用,這樣八匠,在GC掃描時就可以直接獲取這些信息了侦鹏。

3、SafePoint

在 OopMap 的協(xié)助下臀叙,HotSpot 可以快速且準(zhǔn)確地完成 GC Roots 枚舉略水。但是隨著程序的運行,引用關(guān)系會隨之變化劝萤,虛擬機不可能為每一個執(zhí)行節(jié)點都生成 OopMap 來記錄引用關(guān)系渊涝,那將需要大量額外的空間,GC的成本將會變得很高。實際上跨释,HotSpot虛擬機也的確沒有為每一條指令都生成OopMap胸私,只是在特定位置記錄這些信息,這些位置稱為“安全點”(Safepoint)鳖谈,即程序執(zhí)行時并非在所有地方都停頓下來開始GC岁疼,只有在到達(dá)安全點時才能暫停。

Safepoint既不能選的太少導(dǎo)致GC等待太長時間缆娃,也不能過于頻繁以至于過分增大運行時的負(fù)荷捷绒。安全點一般選取在方法調(diào)用、循環(huán)跳轉(zhuǎn)贯要、異常跳轉(zhuǎn)等讓程序長時間執(zhí)行的指令暖侨。

對于Safepoint,另一個需要考慮的問題是如何在GC發(fā)生時讓所有線程(這里不包括執(zhí)行JNI調(diào)用的線程)都“跑”到最近的安全點上再停頓下來崇渗。主動式中斷的思想是當(dāng)GC需要中斷線程的時候字逗,不直接對線程操作,僅僅簡單的設(shè)置一個標(biāo)志宅广,各線程執(zhí)行時主動去輪詢這個標(biāo)志葫掉,當(dāng)發(fā)現(xiàn)中斷標(biāo)志為真時就自己中斷掛起。輪詢標(biāo)志的位置和安全點是重合的跟狱,另外再加上創(chuàng)建對象需要分配內(nèi)存的地方挖息。

4、SafeRegion

Safepoint機制保證了程序執(zhí)行時兽肤,在不太長的時間內(nèi)就會遇到可進(jìn)入GC的Safepoint。但是程序不執(zhí)行的時候呢绪抛?不執(zhí)行就是程序沒有分配到CPU時間资铡,典型的例子就是線程處于Sleep狀態(tài)或Blocked狀態(tài),這時候線程無法響應(yīng)JVM的中斷請求幢码,“走”到安全的地方再掛起笤休。JVM顯然也不會等待線程重新分配CPU時間。對于這種情況就需要安全區(qū)域(Safe Region)來解決症副。

安全區(qū)域是指在一段代碼片段中引用關(guān)系不會發(fā)生變化店雅,在這個區(qū)域的任意地方開始GC都是安全的。我們也把Safe Region稱為擴展的Safepoint贞铣。

在線程執(zhí)行到Safe Region中的代碼的時候闹啦,首先標(biāo)識自己進(jìn)入了Safe Region,這樣辕坝,當(dāng)在這段時間里JVM要發(fā)起GC時窍奋,就不用考慮標(biāo)識自己為Safe Region狀態(tài)的線程了。在線程要離開Safe Region時,它要檢查系統(tǒng)是否已經(jīng)完成了根節(jié)點枚舉()或者整個GC過程如果完成了琳袄,那線程就繼續(xù)執(zhí)行江场,否則它就必須等待收到可以安全離開Safe Region的信號為止。

四窖逗、垃圾收集器

參考博客:https://blog.csdn.net/tjiyu/article/details/53983650

垃圾收集器是垃圾回收算法(標(biāo)記-清除算法址否、復(fù)制算法、標(biāo)記-整理算法)的具體實現(xiàn)碎紊,不同商家佑附、不同版本的JVM所提供的垃圾收集器可能會有很在差別,下面主要介紹HotSpot虛擬機中的垃圾收集器矮慕。

本節(jié)介紹這些收集器的特性帮匾、基本原理和使用場景。沒有最好的收集器痴鳄,更沒有萬能的收集瘟斜,選擇的只能是適合具體應(yīng)用場景的收集器。

1痪寻、垃圾收集器組合

JDK7/8后螺句,HotSpot虛擬機所有收集器及組合(連線),如下圖:

image
  • 圖中展示了7種不同分代的收集器:

Serial橡类、ParNew蛇尚、Parallel Scavenge、Serial Old顾画、Parallel Old取劫、CMS、G1研侣;

  • 它們所處區(qū)域谱邪,表明其是屬于新生代收集器還是老年代收集器:

    新生代收集器:Serial、ParNew庶诡、Parallel Scavenge

    老年代收集器:Serial Old惦银、Parallel Old、CMS

    整堆收集器:G1

  • 兩個收集器間有連線末誓,表明它們可以搭配使用

Serial/Serial Old扯俱、Serial/CMS、ParNew/Serial Old喇澡、ParNew/CMS迅栅、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old晴玖、G1库继;

  • 其中Serial Old作為CMS出現(xiàn)"Concurrent Mode Failure"失敗的后備預(yù)案箩艺。

2、 Minor GC 和 Full GC

  • Minor GC

又稱新生代GC宪萄,指發(fā)生在新生代的垃圾收集動作艺谆;

因為Java對象大多是朝生夕滅,所以Minor GC非常頻繁拜英,一般回收速度也比較快静汤;

  • Full GC

又稱Major GC或老年代GC,指發(fā)生在老年代的GC居凶;

出現(xiàn)Full GC經(jīng)常會伴隨至少一次的Minor GC(不是絕對虫给,Parallel Sacvenge收集器就可以選擇設(shè)置Major GC策略);

Major GC速度一般比Minor GC慢10倍以上侠碧;

3抹估、Serial 收集器(*選學(xué))

  • Serial(串行)垃圾收集器是最基本、發(fā)展歷史最悠久的收集器弄兜;JDK1.3.1前是HotSpot新生代收集的唯一選擇药蜻。

  • 特點: 針對新生代;采用復(fù)制算法替饿; 單線程收集语泽;進(jìn)行垃圾收集時,必須暫停所有工作線程视卢,直到完成踱卵。

  • 應(yīng)用場景:依然是HotSpot在Client模式下默認(rèn)的新生代收集器;也有優(yōu)于其他收集器的地方:簡單高效(與其他收集器的單線程相比)据过;對于限定單個CPU的環(huán)境來說惋砂,Serial收集器沒有線程交互(切換)開銷,可以獲得最高的單線程收集效率绳锅;在用戶的桌面應(yīng)用場景中西饵,可用內(nèi)存一般不大(幾十M至一兩百M),可以在較短時間內(nèi)完成垃圾收集(幾十MS至一百多MS),只要不頻繁發(fā)生榨呆,這是可以接受的。

  • Serial/Serial Old組合收集器運行示意圖如下:

image

4庸队、ParNew收集器

  • ParNew垃圾收集器是Serial收集器的多線程版本积蜻。

  • 除了多線程外,其余的行為彻消、特點和Serial收集器一樣竿拆;如Serial收集器可用控制參數(shù)、收集算法宾尚、Stop The World丙笋、內(nèi)存分配規(guī)則谢澈、回收策略等;兩個收集器共用了不少代碼御板;

  • 應(yīng)用場景:在Server模式下锥忿,ParNew收集器是一個非常重要的收集器,因為除Serial外怠肋,目前只有它能與CMS收集器配合工作敬鬓;但在單個CPU環(huán)境中,不會比Serail收集器有更好的效果笙各,因為存在線程交互開銷钉答。

  • ParNew/Serial Old組合收集器運行示意圖如下:
    image

5、Parallel Scavenge收集器

  • Parallel Scavenge垃圾收集器因為與吞吐量關(guān)系密切杈抢,也稱為吞吐量收集器(Throughput Collector)数尿。

  • 特點:有一些特點與 ParNew 收集器相似,新生代收集器惶楼;采用復(fù)制算法右蹦;多線程收集。主要特點是:Parallel Scavenge 收集器的目標(biāo)則是達(dá)一個可控制的吞吐量(Throughput)鲫懒,系統(tǒng)通過調(diào)節(jié)新生老生代空間比例等嫩实,來調(diào)節(jié)吞吐量。

  • Parallel Scavenge 收集器提供了兩個參數(shù)用來精確控制吞吐量窥岩,分別是控制最大垃圾收集停頓時間的-XX:MaxGCPauseMillis甲献,以及直接設(shè)置吞吐量大小 -XXGCTimeRatio。停頓時間和吞吐量存在相互制約的關(guān)系:GC停頓時間縮短是以犧牲吞吐量和新生代空間來換取的:系統(tǒng)把新生代調(diào)小一點颂翼,收集300MB新生代肯定比收集500MB快吧晃洒,這也直接導(dǎo)致垃圾收集發(fā)生的更頻繁一些,原來10收集一次朦乏,現(xiàn)在變成5秒收集一次球及,每次停頓70毫秒,停頓時間的確是下降了呻疹,但吞吐量也降下來了吃引。

  • 應(yīng)用場景: 高吞吐量為目標(biāo),即減少垃圾收集時間刽锤,讓用戶代碼獲得更長的運行時間镊尺;當(dāng)應(yīng)用程序運行在具有多個CPU上,對暫停時間沒有特別高的要求時并思,即程序主要在后臺進(jìn)行計算庐氮,而不需要與用戶進(jìn)行太多交互;例如宋彼,那些執(zhí)行批量處理弄砍、訂單處理仙畦、工資支付、科學(xué)計算的應(yīng)用程序音婶;

  • 吞吐量與收集器關(guān)注點說明

    (A)慨畸、吞吐量(Throughput)

    CPU用于運行用戶代碼的時間與CPU總消耗時間的比值;

    吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)桃熄;

    高吞吐量即減少垃圾收集時間先口,讓用戶代碼獲得更長的運行時間;

    (B)瞳收、垃圾收集器期望的目標(biāo)(關(guān)注點)

    (1)碉京、停頓時間

    停頓時間越短就適合需要與用戶交互的程序;

    良好的響應(yīng)速度能提升用戶體驗螟深;

    (2)谐宙、吞吐量

    高吞吐量則可以高效率地利用CPU時間,盡快完成運算的任務(wù)界弧;

    主要適合在后臺計算而不需要太多交互的任務(wù)凡蜻;

    (3)、覆蓋區(qū)(Footprint)

    在達(dá)到前面兩個目標(biāo)的情況下垢箕,盡量減少堆的內(nèi)存空間划栓;

    可以獲得更好的空間局部性;

6条获、Serial Old收集器(*選學(xué))

  • Serial Old是 Serial收集器的老年代版本忠荞;

  • 針對老年代;采用"標(biāo)記-整理"算法(還有壓縮帅掘,Mark-Sweep-Compact)委煤;單線程收集;

  • 應(yīng)用場景:主要用于Client模式修档;而在Server模式有兩大用途:(A)碧绞、在JDK1.5及之前,與Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配)吱窝;(B)讥邻、作為CMS收集器的后備預(yù)案,在并發(fā)收集發(fā)生Concurrent Mode Failure時使用(后面詳解)院峡;

7兴使、Parallel Old收集器

  • Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本; JDK1.6中才開始提供撕予;

  • 針對老年代鲫惶;采用"標(biāo)記-整理"算法蜈首;多線程收集实抡;

  • JDK1.6及之后用來代替老年代的Serial Old收集器欠母;特別是在Server模式,多CPU的情況下吆寨;這樣在注重吞吐量以及CPU資源敏感的場景赏淌,就有了Parallel Scavenge加Parallel Old收集器的"給力"應(yīng)用組合;

8啄清、CMS收集器

  • 并發(fā)標(biāo)記清理(Concurrent Mark Sweep六水,CMS)收集器也稱為并發(fā)低停頓收集器(Concurrent Low Pause Collector)或低延遲(low-latency)垃圾收集器;CMS等收集器的關(guān)注點是盡可能地縮短垃圾收集時用戶線程的停頓時間辣卒;

  • 特點:針對老年代掷贾;基于"標(biāo)記-清除"算法(不進(jìn)行壓縮操作,產(chǎn)生內(nèi)存碎片)荣茫;以獲取最短回收停頓時間為目標(biāo)想帅;并發(fā)收集、低停頓啡莉;需要更多的內(nèi)存港准;是HotSpot在JDK1.5推出的第一款真正意義上的并發(fā)(Concurrent)收集器;第一次實現(xiàn)了讓垃圾收集線程與用戶線程(基本上)同時工作咧欣;

  • 缺點:

    (A)對CPU資源非常敏感:并發(fā)收集雖然不會暫停用戶線程浅缸,但因為占用一部分CPU資源,還是會導(dǎo)致應(yīng)用程序變慢魄咕,總吞吐量降低衩椒。

    (B)無法處理浮動垃圾,可能出現(xiàn)"Concurrent Mode Failure"失敗:在并發(fā)清除時蚕礼,用戶線程新產(chǎn)生的垃圾烟具,稱為浮動垃圾;這使得并發(fā)清除時需要預(yù)留一定的內(nèi)存空間奠蹬,不能像其他收集器在老年代幾乎填滿再進(jìn)行收集朝聋;如果CMS預(yù)留內(nèi)存空間無法滿足程序需要,就會出現(xiàn)一次"Concurrent Mode Failure"失敹谠辍冀痕;這時JVM啟用后備預(yù)案:臨時啟用Serail Old收集器,而導(dǎo)致另一次Full GC的產(chǎn)生狸演;

    (C)產(chǎn)生大量內(nèi)存碎片:由于CMS基于"標(biāo)記-清除"算法言蛇,清除后不進(jìn)行壓縮操作;

  • 適用場景:與用戶交互較多的場景宵距,希望系統(tǒng)停頓時間最短腊尚,注重服務(wù)的響應(yīng)速度;以給用戶帶來較好的體驗满哪;如常見WEB婿斥、B/S系統(tǒng)的服務(wù)器上的應(yīng)用劝篷;

  • CMS收集器運行示意圖如下:
    image

9、G1收集器

  • G1(Garbage-First)是JDK7-u4才推出商用的收集器民宿;

  • 特點:

    (A)并行與并發(fā):能充分利用多CPU娇妓、多核環(huán)境下的硬件優(yōu)勢;可以并行來縮短"Stop The World"停頓時間活鹰;也可以并發(fā)讓垃圾收集與用戶程序同時進(jìn)行哈恰;

    (B)分代收集,收集范圍包括新生代和老年代 :能獨立管理整個GC堆(新生代和老年代)志群,而不需要與其他收集器搭配着绷;能夠采用不同方式處理不同時期的對象;雖然保留分代概念锌云,但Java堆的內(nèi)存布局有很大差別蓬戚;將整個堆劃分為多個大小相等的獨立區(qū)域(Region);新生代和老年代不再是物理隔離宾抓,它們都是一部分Region(不需要連續(xù))的集合子漩;

    (C)結(jié)合多種垃圾收集算法,空間整合石洗,不產(chǎn)生碎片幢泼。從整體看,是基于標(biāo)記-整理算法讲衫;從局部(兩個Region間)看缕棵,是基于復(fù)制算法;這是一種類似火車算法的實現(xiàn)涉兽;

    (D)可預(yù)測的停頓:低停頓的同時實現(xiàn)高吞吐量G1除了追求低停頓處招驴,還能建立可預(yù)測的停頓時間模型;可以明確指定M毫秒時間片內(nèi)枷畏,垃圾收集消耗的時間不超過N毫秒别厘;

  • 應(yīng)用場景:面向服務(wù)端應(yīng)用,針對具有大內(nèi)存拥诡、多處理器的機器触趴;最主要的應(yīng)用是為需要低GC延遲,并具有大堆的應(yīng)用程序提供解決方案渴肉;如:在堆大小約6GB或更大時冗懦,可預(yù)測的暫停時間可以低于0.5秒; 用來替換掉JDK1.5中的CMS收集器仇祭;在下面的情況時披蕉,使用G1可能比CMS好:

    (1)超過50%的Java堆被活動數(shù)據(jù)占用;

    (2)對象分配頻率或年代提升頻率變化很大;

    (3)GC停頓時間過長(長于0.5至1秒)没讲。 是否一定采用G1呢承冰?也未必:如果現(xiàn)在采用的收集器沒有出現(xiàn)問題,不用急著去選擇G1食零;如果應(yīng)用程序追求低停頓,可以嘗試選擇G1寂屏;是否代替CMS需要實際場景測試才知道贰谣。

  • G1 收集器運行示意圖如下:
    image

五、內(nèi)存分配與回收策略

  • 對象優(yōu)先在 Eden 分配

  • 大對象直接進(jìn)入老年代:所謂的大對象是指迁霎,需要大量連續(xù)內(nèi)存空間的Java對象吱抚,最典型的大對象就是那種很長的字符串以及數(shù)組,經(jīng)常出現(xiàn)大對象容易導(dǎo)致內(nèi)存還有不少空間時就提前觸發(fā)垃圾收集以獲取足夠的連續(xù)內(nèi)存空間來存放他們考廉。虛擬機提供了一個-XX:PretenureSizeThreshold參數(shù)秘豹,令大于這個設(shè)置值的對象直接在老年代分配。這樣做的目的是避免在Eden區(qū)以及兩個Survivor區(qū)之間發(fā)生大量的內(nèi)存復(fù)制(新生代使用復(fù)制算法收集內(nèi)存)昌粤。

  • 長期存活的對象將進(jìn)入老年代:虛擬機給每個對象定義了一個對象年齡計數(shù)器既绕。如果對象在Eden出生,并經(jīng)過第一次Minor GC后仍然存活涮坐,并且能被Survivor所容納的話凄贩,將被移動到Survivor區(qū),并且對象年齡設(shè)置為1袱讹。對象在Survivor區(qū)中沒“熬過”一次Minor GC疲扎,年齡就增加1,當(dāng)它的年齡增加到一定程度(默認(rèn)15歲)捷雕,就將會晉升到老年代中椒丧。對象晉升到老年代的年齡閥值,可以通過參數(shù)-XX:MaxTenuringThreshold設(shè)置救巷。

  • 動態(tài)對象年齡判定:為了能更好的適應(yīng)不同程序的內(nèi)存狀況壶熏,虛擬機并不是永遠(yuǎn)地要求對象的年齡必須達(dá)到了MaxTenuringThreshold才能晉升到老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半浦译,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代久橙,無須等到MaxTenuringThreshold中要求的年齡。

  • 空間分配擔(dān)保:在發(fā)生Minor GC之前管怠,虛擬機會首先檢查老年代最大可用連續(xù)空間是否大于新生代所有對象總空間淆衷,如果這個條件成立,那么Minor GC可用確保是安全的渤弛。如果不成立祝拯,則虛擬機會查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗。如果允許,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小佳头,如果大于鹰贵,將嘗試第一次Minor GC,即使這次Minor GC是有風(fēng)險的康嘉;如果小于碉输,或者HandlePromotionFailure設(shè)置為不允許冒險,那這時也要改為進(jìn)行一次Full GC亭珍。如果出現(xiàn)HandlePromotionFailure失敗敷钾,那就只好在失敗之后重新發(fā)起一次Full GC。雖然擔(dān)保失敗時繞的圈子是最大的肄梨,但大部分情況下都還是會將HandlePromotionFailure開關(guān)打開阻荒,避免Full GC過于頻繁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末众羡,一起剝皮案震驚了整個濱河市侨赡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粱侣,老刑警劉巖羊壹,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異齐婴,居然都是意外死亡舶掖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門尔店,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眨攘,“玉大人,你說我怎么就攤上這事嚣州■晔郏” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵该肴,是天一觀的道長情竹。 經(jīng)常有香客問我,道長匀哄,這世上最難降的妖魔是什么秦效? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮涎嚼,結(jié)果婚禮上阱州,老公的妹妹穿的比我還像新娘。我一直安慰自己法梯,他們只是感情好苔货,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布犀概。 她就那樣靜靜地躺著,像睡著了一般夜惭。 火紅的嫁衣襯著肌膚如雪姻灶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天诈茧,我揣著相機與錄音产喉,去河邊找鬼。 笑死敢会,一個胖子當(dāng)著我的面吹牛曾沈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播走触,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疤苹!你這毒婦竟也來了互广?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤卧土,失蹤者是張志新(化名)和其女友劉穎惫皱,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尤莺,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡旅敷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了颤霎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媳谁。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖友酱,靈堂內(nèi)的尸體忽然破棺而出晴音,到底是詐尸還是另有隱情,我是刑警寧澤缔杉,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布锤躁,位于F島的核電站,受9級特大地震影響或详,放射性物質(zhì)發(fā)生泄漏系羞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一霸琴、第九天 我趴在偏房一處隱蔽的房頂上張望椒振。 院中可真熱鬧,春花似錦梧乘、人聲如沸杠人。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗡善。三九已至辑莫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罩引,已是汗流浹背各吨。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留袁铐,地道東北人揭蜒。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像剔桨,于是被迫代替她去往敵國和親屉更。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

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