前置概念
- Java GC诬滩、新生代凉倚、老年代
Java 中的堆是 JVM 所管理的最大的一塊內(nèi)存空間渺氧,主要用于存放各種類的實(shí)例對象友驮。
在 Java 中漂羊,堆被劃分成兩個(gè)不同的區(qū)域:新生代 ( Young )、老年代 ( Old )卸留。
新生代 ( Young ) 又被劃分為三個(gè)區(qū)域:Eden走越、From Survivor、To Survivor耻瑟。
這樣劃分的目的是為了使 JVM 能夠更好的管理堆內(nèi)存中的對象旨指,包括內(nèi)存的分配以及回收。 - 堆空間的分配
堆大小 = 新生代 + 老年代喳整。其中谆构,堆的大小可以通過參數(shù) –Xms、-Xmx 來指定算柳。
默認(rèn)的低淡,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ( 該值可以通過參數(shù) –XX:NewRatio 來指定 ),
即:新生代 ( Young ) = 1/3 的堆空間大小瞬项。老年代 ( Old ) = 2/3 的堆空間大小蔗蹋。
其中,新生代 ( Young ) 被細(xì)分為 Eden 和 兩個(gè) Survivor 區(qū)域囱淋,這兩個(gè) Survivor 區(qū)域分別被命名為 from 和 to猪杭,以示區(qū)分
默認(rèn)的,Edem : from : to = 8 :1 : 1 ( 可以通過參數(shù)–XX:SurvivorRatio 來設(shè)定 )妥衣,即: Eden = 8/10 的新生代空間大小皂吮,from = to = 1/10 的新生代空間大小。
JVM 每次只會使用 Eden 和其中的一塊 Survivor 區(qū)域來為對象服務(wù)税手,所以無論什么時(shí)候蜂筹,總是有一塊Survivor區(qū)域是空閑著的。
因此芦倒,新生代實(shí)際可用的內(nèi)存空間為 9/10 ( 即90% )的新生代空間艺挪。
一、minor GC和full GC的區(qū)別
新生代GC(minor GC):指發(fā)生在新生代的垃圾收集動作兵扬,minor GC非常頻繁麻裳,回收速度也比較快。
新生代通常存活時(shí)間較短器钟,因此基于復(fù)制算法來進(jìn)行回收津坑,所謂復(fù)制算法就是掃描出存活的對象,并復(fù)制到一塊新的完全未使用的空間中.老生代GC(full GC/major GC):指發(fā)生在老生代的垃圾收集動作傲霸,出現(xiàn)了 Major GC 經(jīng)常會伴隨至少一次的 Minor GC(并非絕對)疆瑰,Major GC 的速度一般會比 Minor GC 的慢 10 倍以上。
舊生代與新生代不同昙啄,對象存活的時(shí)間比較長乃摹,比較穩(wěn)定,因此采用標(biāo)記(Mark)算法來進(jìn)行回收跟衅,所謂標(biāo)記就是掃描出存活的對象孵睬,然后再進(jìn)行回收未被標(biāo)記的對象,回收后對用空出的空間要么進(jìn)行合并伶跷,要么標(biāo)記出來便于下次進(jìn)行分配掰读,總之就是要減少內(nèi)存碎片帶來的效率損耗
二、minorGC過程詳解
minor GC整體過程如下:
1叭莫,初始階段蹈集,新創(chuàng)建的對象被分配到Eden區(qū),survivor的兩塊空間都是空的雇初。
2拢肆,當(dāng)Eden區(qū)滿了的時(shí)候,minor GC觸發(fā),經(jīng)過掃描與標(biāo)記郭怪,存活的對象被復(fù)制到S0支示,不存活的對象被回收, 并且存活的對象年齡都增大一歲鄙才。
3颂鸿,在下一次的Minor GC中,Eden區(qū)的情況和上面一致攒庵,沒有引用的對象被回收嘴纺,存活的對象被復(fù)制到survivor區(qū)。當(dāng)Eden 和 s0區(qū)空間滿了浓冒,S0的所有的數(shù)據(jù)都被復(fù)制到S1栽渴,需要注意的是,在上次minor GC過程中移動到S0中的兩個(gè)對象在復(fù)制到S1后其年齡要加1稳懒。此時(shí)Eden區(qū)S0區(qū)被清空闲擦,所有存活的數(shù)據(jù)都復(fù)制到了S1區(qū),并且S1區(qū)存在著年齡不一樣的對象僚祷。
4佛致,再下一次MinorGC則重復(fù)這個(gè)過程,這一次survivor的兩個(gè)區(qū)對換辙谜,存活的對象被復(fù)制到S0俺榆,存活的對象年齡加1,Eden區(qū)和另一個(gè)survivor區(qū)被清空装哆。
5罐脊,再經(jīng)過幾次Minor GC之后,當(dāng)存活對象的年齡達(dá)到一個(gè)閾值之后(-XX:MaxTenuringThreshold默認(rèn)是15)蜕琴,就會被從年輕代Promotion到老年代萍桌。
6, 隨著MinorGC一次又一次的進(jìn)行凌简,不斷會有新的對象被promote到老年代上炎。
7,上面基本上覆蓋了整個(gè)年輕代所有的回收過程雏搂。最終藕施,MajorGC將會在老年代發(fā)生,老年代的空間將會被清除和壓縮(標(biāo)記-清除或者標(biāo)記整理)凸郑。
整體描述
大部分情況裳食,對象都會首先在 Eden 區(qū)域分配,在一次新生代垃圾回收后芙沥,如果對象還存活诲祸,則會進(jìn)入 s1(“To”)浊吏,并且對象的年齡還會加 1(Eden 區(qū)->Survivor 區(qū)后對象的初始年齡變?yōu)?1),當(dāng)它的年齡增加到一定程度(默認(rèn)為 15 歲)救氯,就會被晉升到老年代中找田。對象晉升到老年代的年齡閾值,可以通過參數(shù) -XX:MaxTenuringThreshold 來設(shè)置径密。經(jīng)過這次GC后午阵,Eden區(qū)和"From"區(qū)已經(jīng)被清空躺孝。這個(gè)時(shí)候享扔,“From"和"To"會交換他們的角色,也就是新的"To"就是上次GC前的“From”植袍,新的"From"就是上次GC前的"To”惧眠。不管怎樣,都會保證名為To的Survivor區(qū)域是空的于个。Minor GC會一直重復(fù)這樣的過程氛魁,直到“To”區(qū)被填滿,"To"區(qū)被填滿之后厅篓,會將所有對象移動到年老代中秀存。
總結(jié)
從上面的過程可以看出,Eden區(qū)是連續(xù)的空間羽氮,且Survivor總有一個(gè)為空或链。經(jīng)過一次GC和復(fù)制,一個(gè)Survivor中保存著當(dāng)前還活著的對象档押,而Eden區(qū)和另一個(gè)Survivor區(qū)的內(nèi)容都不再需要了澳盐,可以直接清空,到下一次GC時(shí)令宿,兩個(gè)Survivor的角色再互換叼耙。因此,這種方式分配內(nèi)存和清理內(nèi)存的效率都極高粒没,這種垃圾回收的方式就是著名的“停止-復(fù)制(Stop-and-copy)”清理法(將Eden區(qū)和一個(gè)Survivor中仍然存活的對象拷貝到另一個(gè)Survivor中)筛婉,這不代表著停止復(fù)制清理法很高效,其實(shí)癞松,它也只在這種情況下(基于大部分對象存活周期很短的事實(shí))高效爽撒,如果在老年代采用停止復(fù)制,則是非常不合適的拦惋。
老年代存儲的對象比年輕代多得多匆浙,而且不乏大對象,對老年代進(jìn)行內(nèi)存清理時(shí)厕妖,如果使用停止-復(fù)制算法首尼,則相當(dāng)?shù)托АR话悖夏甏玫乃惴ㄊ菢?biāo)記-壓縮算法软能,即:標(biāo)記出仍然存活的對象(存在引用的)迎捺,將所有存活的對象向一端移動,以保證內(nèi)存的連續(xù)查排。在發(fā)生Minor GC時(shí)凳枝,虛擬機(jī)會檢查每次晉升進(jìn)入老年代的大小是否大于老年代的剩余空間大小,如果大于跋核,則直接觸發(fā)一次Full GC岖瑰,否則,就查看是否設(shè)置了-XX:+HandlePromotionFailure(允許擔(dān)保失斏按)蹋订,如果允許,則只會進(jìn)行MinorGC刻伊,此時(shí)可以容忍內(nèi)存分配失斅督洹;如果不允許捶箱,則仍然進(jìn)行Full GC(這代表著如果設(shè)置-XX:+Handle PromotionFailure智什,則觸發(fā)MinorGC就會同時(shí)觸發(fā)Full GC,哪怕老年代還有很多內(nèi)存丁屎,所以荠锭,最好不要這樣做)。
三悦屏、GC觸發(fā)條件
Minor GC觸發(fā)條件:
- Eden區(qū)滿時(shí)
Full GC觸發(fā)條件:
- 調(diào)用System.gc時(shí)节沦,系統(tǒng)建議執(zhí)行Full GC,但是不必然執(zhí)行
- 老年代空間不足
- 方法去空間不足
- 通過Minor GC后進(jìn)入老年代的平均大小大于老年代的可用內(nèi)存
- 由Eden區(qū)础爬、From Space區(qū)向To Space區(qū)復(fù)制時(shí)甫贯,對象大小大于To Space可用內(nèi)存,則把該對象轉(zhuǎn)存到老年代看蚜,且老年代的可用內(nèi)存小于該對象大小叫搁。
四、對象進(jìn)入老年代的四種情況
- 假如進(jìn)行Minor GC時(shí)發(fā)現(xiàn)供炎,存活的對象在ToSpace區(qū)中存不下渴逻,那么把存活的對象存入老年代
- 大對象直接進(jìn)入老年代
假設(shè)新創(chuàng)建的對象很大,比如為5M(這個(gè)值可以通過PretenureSizeThreshold這個(gè)參數(shù)進(jìn)行設(shè)置音诫,默認(rèn)3M)惨奕,那么即使Eden區(qū)有足夠的空間來存放,也不會存放在Eden區(qū)竭钝,而是直接存入老年代梨撞。 - 長期存活的對象將進(jìn)入老年代
此外雹洗,如果對象在Eden出生并且經(jīng)過1次Minor GC后仍然存活,并且能被To區(qū)容納卧波,那么將被移動到To區(qū)时肿,并且把對象的年齡設(shè)置為1,對象沒"熬過"一次Minor GC(沒有被回收港粱,也沒有因?yàn)門o區(qū)沒有空間而被移動到老年代中)螃成,年齡就增加一歲,當(dāng)它的年齡增加到一定程度(默認(rèn)15歲查坪,配置參數(shù)-XX:MaxTenuringThreshold)寸宏,就會被晉升到老年代中。 - 動態(tài)對象年齡判定
還有一種情況咪惠,如果在From空間中击吱,相同年齡所有對象的大小總和大于Survivor空間的一半淋淀,那么年齡大于等于該年齡的對象就會被移動到老年代遥昧,而不用等到15歲(默認(rèn))。