內(nèi)存分配策略

前言

在開始介紹內(nèi)存分配策略之前碾褂,先啰嗦一下gc日志相關(guān)內(nèi)容兽间,要知道會(huì)讀gc日志是處理java虛擬機(jī)內(nèi)存問題的一項(xiàng)基本技能。接下來以一段gc日志為例正塌,詳細(xì)介紹下日志相關(guān)內(nèi)容:

[GC (Allocation Failure) --[PSYoungGen: 8192K->8192K(9216K)] 12288K->16392K(19456K), 0.0038111 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 8192K->2731K(9216K)] [ParOldGen: 8200K->8193K(10240K)] 16392K->10924K(19456K), [Metaspace: 3334K->3334K(1056768K)], 0.0056151 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 

:其實(shí)gc日志的格式是跟垃圾收集器有關(guān)的嘀略,不同的收集器,它們的格式可能都是不一樣的乓诽,但是JVM的設(shè)計(jì)者為了方便程序員們閱讀帜羊,將各個(gè)收集器的日志做了格式統(tǒng)一。

  1. gc日志開頭的[GC[Full GC代表這次垃圾收集的類型鸠天,需要注意的是讼育,它并不是用來區(qū)分是新生代的gc還是老年代gc的,如果是Full GC稠集,只能說明這次gc是發(fā)生了STW(Stop-The-World)的奶段;

  2. 接下來會(huì)看到[PSYoungGen[ParOldGen剥纷,[Metaspace表示gc發(fā)生的區(qū)域痹籍,當(dāng)然這里的區(qū)域名與垃圾收集器是相關(guān)的:

    • 如果是Serial收集器,那么新生代名稱為Default New Generation晦鞋,gc日志顯示[DefNew蹲缠;
    • 如果是ParNew收集器,新生代的名稱變成Parallel New Gneration鳖宾,gc日志顯示[ParNew吼砂;
    • 如果是Parallel Scavenge收集器,gc日志則顯示[PSYoungGen
  3. 區(qū)域名稱后緊跟著8192K->8192K(9216K)鼎文,它的意思是在gc前該內(nèi)存區(qū)域已使用的量 -> gc后該內(nèi)存區(qū)域的使用量(該內(nèi)存區(qū)域總?cè)萘浚T诜嚼ㄌ?hào)后面緊跟著12288K->16392K(19456K)因俐,它表示gc heap已使用的量 -> gc后heap的使用量(heap的總?cè)萘浚?/p>

  4. 在各個(gè)區(qū)域的gc相關(guān)內(nèi)存變化之后拇惋,會(huì)給出該內(nèi)存區(qū)域gc所用時(shí)間,有的收集器會(huì)給出具體的gc耗時(shí)時(shí)間數(shù)據(jù)抹剩,比如[Times: user=0.02 sys=0.00, real=0.01 secs]撑帖,可以看到,該時(shí)間數(shù)據(jù)包括3個(gè)時(shí)間:

    • user:用戶態(tài)消耗的cpu時(shí)間澳眷;
    • sys:內(nèi)核態(tài)消耗的cpu時(shí)間胡嘿;
    • real:操作從開始到結(jié)束所經(jīng)過的實(shí)際時(shí)間

:需要注意的是,real時(shí)間包括各種非運(yùn)算的等待耗時(shí)钳踊,比如等待磁盤I/O衷敌,但是cpu時(shí)間是不包括這些耗時(shí)的勿侯。可能熟悉gc日志的同學(xué)可能會(huì)問缴罗,既然real的時(shí)間包括等待時(shí)間助琐,user和sys不包括等待時(shí)間,那為什么好多時(shí)候user或者sys的時(shí)間會(huì)超過real呢面氓?我們現(xiàn)在絕大多數(shù)服務(wù)器都是多cpu或者多核的兵钮,當(dāng)多個(gè)線程操作時(shí),user和sys會(huì)疊加這些cpu時(shí)間舌界,所以看到user或者sys的時(shí)間超過real是很正常的掘譬。

啰里吧嗦介紹完gc日志后,接下來我們就可以進(jìn)入正題呻拌,看一下JVM對(duì)象內(nèi)存的分配策略葱轩。

內(nèi)存分配策略

對(duì)象內(nèi)存的分配,簡單點(diǎn)兒說柏锄,就是在heap上分配內(nèi)存(JIT編譯可能間接在棧上分配)酿箭,對(duì)象首先會(huì)在Eden區(qū)分配,當(dāng)然趾娃,如果啟動(dòng)了本地線程分配緩存缭嫡,則優(yōu)先在線程的TLAB上分配,同時(shí)抬闷,也會(huì)有少數(shù)情況會(huì)在old區(qū)分配妇蛀,具體的分配細(xì)節(jié)跟垃圾收集器以及JVM內(nèi)存相關(guān)參數(shù)相關(guān)。接下來就根據(jù)實(shí)例具體分析一下相關(guān)分配策略笤成。

1. 對(duì)象首先在Eden區(qū)分配

在大多數(shù)情況下评架,對(duì)象都優(yōu)先在eden區(qū)分配內(nèi)存,當(dāng)eden區(qū)內(nèi)存空間不夠時(shí)炕泳,JVM會(huì)發(fā)起一次Monitor GC(對(duì)young區(qū)的gc)

案例1

在案例1中纵诞,通過JVM參數(shù)-Xms20M -Xmx20M -Xmn10M限制了heap的大小為20M并且不可擴(kuò)展,young區(qū)的大小為10M培遵,剩下的10M給old區(qū)浙芙,在main方法中,創(chuàng)建byte1到btye4四個(gè)對(duì)象籽腕,一共10M嗡呼,我們看一下會(huì)發(fā)生什么?
案例1.gc日志

從gc日志可以看出:

  1. 在分配bytes1到bytes3后皇耗,eden區(qū)沒有額外的空間南窗,再創(chuàng)建bytes4的時(shí)候,此時(shí)eden區(qū)內(nèi)存不夠,觸發(fā)Minitor GC万伤,本次gc結(jié)束后窒悔,yong區(qū)的6441k變成872k,但是由于bytes1到bytes3對(duì)象都是存活的壕翩,所以總得內(nèi)存量其實(shí)并沒有減少蛉迹;

  2. 在發(fā)生Monitor GC的過程中,由于Survivor空間只有1M放妈,不足以放下bytes1到bytes3的任何一個(gè)對(duì)象北救,此時(shí),通過分配擔(dān)保機(jī)制芜抒,會(huì)提前進(jìn)入old區(qū)珍策;

  3. 這次GC結(jié)束后,bytes4被順利分配在eden區(qū)宅倒,此時(shí)攘宙,eden區(qū)占用6M,Survivor空閑拐迁,old區(qū)被占用4M蹭劈。

2. 大對(duì)象直接進(jìn)入老年代

:大對(duì)象定義:所謂的大對(duì)象,其實(shí)就是指需要大量的連續(xù)內(nèi)存空間的對(duì)象线召,比如長度很長的數(shù)組铺韧。

對(duì)于JVM來說,需要分配大對(duì)象是一個(gè)壞消息缓淹,如果程序中經(jīng)常出現(xiàn)大對(duì)象就容易導(dǎo)致gc的提前觸發(fā)哈打。當(dāng)然,JVM提供參數(shù)-XX:PretenureSizeThreshold讯壶,一旦對(duì)象所需內(nèi)存大小大于該參數(shù)配置的閾值料仗,直接在old區(qū)為其分配內(nèi)存空間。當(dāng)然伏蚊,這樣做的目的一則是為了避免gc提前出發(fā)立轧,二則是為了避免在eden區(qū)和Survivor區(qū)發(fā)生大量的內(nèi)存拷貝。接下來還是以一個(gè)簡單的例子驗(yàn)證該規(guī)則躏吊。

案例2

在案例2中肺孵,通過參數(shù)-XX:PretenureSizeThreshold=4194304設(shè)置閾值為4M,一旦待分配對(duì)象大小超過4M颜阐,直接在old區(qū)進(jìn)行內(nèi)存分配。在main方法中吓肋,要?jiǎng)?chuàng)建一個(gè)大小為5M的byte數(shù)組凳怨,我們來看下gc日志,看看這個(gè)對(duì)象是不是直接在old區(qū)分配內(nèi)存的。
案例2.gc日志

從gc日志標(biāo)紅的地方可以看出肤舞,byte4確實(shí)直接被放在了old區(qū)紫新。

:需要注意的是,-XX:PretenureSizeThreshold只對(duì)ParNew和Serial垃圾收集器有效李剖,如果你需要使用該參數(shù)的話芒率,可以使用ParNew + CMS。

3. 長期存活的對(duì)象將進(jìn)入老年代

JVM采用分代收集來管理內(nèi)存篙顺,為了在gc的時(shí)候能夠確認(rèn)哪些對(duì)象要放在young區(qū)偶芍,哪些對(duì)象放在old區(qū),JVM為給每一個(gè)對(duì)象都定義了一個(gè)年齡計(jì)數(shù)器德玫,如果對(duì)象在eden區(qū)被分配內(nèi)存并且經(jīng)過第一次Monitor GC后還存活匪蟀,此時(shí)該對(duì)象會(huì)被移動(dòng)到Survivor區(qū),對(duì)象的年齡被設(shè)置成為1宰僧,該對(duì)象在Survivor區(qū)中每經(jīng)過一次Monitor GC還不被回收材彪,年齡就加1,當(dāng)它的年齡增加達(dá)到一定的值時(shí)(默認(rèn)值是15)琴儿,對(duì)象就會(huì)被晉升到old區(qū)段化。當(dāng)然,JVM提供參數(shù)-XX:MaxTenuringThreshold來設(shè)置對(duì)象晉升到old區(qū)的年齡閾值造成。

案例3

在案例3中显熏,通過-XX:MaxTenuringThreshold=1設(shè)置對(duì)象晉升到old區(qū)的年齡閾值為1,那么谜疤,gc結(jié)束后佃延,bytes1和bytes2均會(huì)進(jìn)入old區(qū),我們看一下gc日志看看是不是這樣夷磕。
案例3.gc日志

從gc日志可以看出履肃,經(jīng)過兩次Monitor GC,bytes1和bytes2均進(jìn)入old區(qū)坐桩,bytes3在eden區(qū)尺棋。

4. 動(dòng)態(tài)年齡判定

雖然JVM要求對(duì)象年齡必須要達(dá)到-XX:MaxTenuringThreshold設(shè)置的閾值才能晉升到old區(qū),但是绵跷,為了更好的適應(yīng)不同的內(nèi)存使用情況膘螟,JVM增加了一個(gè)新的晉升到old區(qū)的條件:如果在Survivor區(qū)中相同年齡的對(duì)象所占內(nèi)存空間大于Survivor區(qū)的一半,不小于該年齡的對(duì)象可以直接進(jìn)入old區(qū)碾局,不需要達(dá)到-XX:MaxTenuringThreshold設(shè)置的閾值荆残。

案例4

gc日志:
案例4.gc日志

從gc日志可以看出,經(jīng)過兩次Monitor GC净当,由于bytes1所占用內(nèi)存空間大于Survivor區(qū)的一半内斯,bytes1和bytes2均進(jìn)入old區(qū)蕴潦。

5. 空間分配擔(dān)保

在發(fā)生Monitor GC之前,JVM會(huì)檢查old區(qū)的最大可用連續(xù)空間是否大于young區(qū)所有對(duì)象總空間俘闯,如果條件成立潭苞,那么Monitor GC一定是安全的,進(jìn)行一次Monitor GC真朗,如果不成立:

  • 在jdk6 update 24之前JVM則會(huì)讀取參數(shù)-XX:-HandlePromotionFailure值判斷是否允許擔(dān)保失敗此疹,如果允許,檢查old區(qū)的最大可用連續(xù)空間是否大于晉升到old區(qū)對(duì)象的平均大小遮婶,如果大于蝗碎,再進(jìn)行一次Monitor GC,如果小于或者不允許擔(dān)保失敗蹭睡,則進(jìn)行一次Full GC衍菱;

  • 在jdk6 update 24之后,雖然JVM還定義參數(shù)-XX:-HandlePromotionFailure肩豁,但是已經(jīng)不會(huì)再使用它脊串,規(guī)則變?yōu)橹灰猳ld區(qū)最大可用連續(xù)空間大于晉升到old區(qū)的對(duì)象的平均大小,就進(jìn)行一次Monitor GC清钥,否則琼锋,進(jìn)行一次Full GC,JVM源碼也可以驗(yàn)證此規(guī)則:

    JVM源碼

看到這里大家估計(jì)還是有點(diǎn)懵祟昭,還是不理解為什么要空間分配擔(dān)保缕坎,接下來就解釋下為什么需要空間分配擔(dān)保。

為什么需要空間分配擔(dān)保篡悟?

注:空間分配擔(dān)保其實(shí)就是JVM確認(rèn)old區(qū)是否可以容納Monitor GC后晉升到old區(qū)的對(duì)象們谜叹。

由于young區(qū)的gc算法是復(fù)制收集算法,為了內(nèi)存的使用率搬葬,JVM只使用其中的一個(gè)Survivor取作為中間轉(zhuǎn)換空間荷腊,當(dāng)出現(xiàn)大量對(duì)象在Monitor GC后還存活的,此時(shí)急凰,Survivor區(qū)無法容納的對(duì)象直接進(jìn)入old區(qū)女仰,在進(jìn)入old區(qū)之前JVM一定要確認(rèn)old區(qū)是否有足夠的剩余空間可以容納這些對(duì)象。由于在回收之前并不知道有多少對(duì)象要進(jìn)入old區(qū)抡锈,JVM設(shè)計(jì)者認(rèn)為可以取每一次Monitor GC晉升到old區(qū)的對(duì)象容量的平均大小為經(jīng)驗(yàn)值疾忍,將該經(jīng)驗(yàn)值與old區(qū)剩余空間比較,來決定是否要進(jìn)行一次Full GC讓old區(qū)釋放出更多的空間床三。但是一罩,取平均值畢竟是一種動(dòng)態(tài)概率手段,如果某一次Monitor GC后存活對(duì)象陡增撇簿,遠(yuǎn)遠(yuǎn)高于平均值擒抛,此時(shí)還是會(huì)擔(dān)保失敗推汽,一旦出現(xiàn)擔(dān)保失敗,JVM會(huì)發(fā)起一次Full GC歧沪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市莲组,隨后出現(xiàn)的幾起案子诊胞,更是在濱河造成了極大的恐慌,老刑警劉巖锹杈,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撵孤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡竭望,警方通過查閱死者的電腦和手機(jī)邪码,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咬清,“玉大人闭专,你說我怎么就攤上這事【缮眨” “怎么了影钉?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掘剪。 經(jīng)常有香客問我平委,道長,這世上最難降的妖魔是什么夺谁? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任廉赔,我火速辦了婚禮,結(jié)果婚禮上匾鸥,老公的妹妹穿的比我還像新娘蜡塌。我一直安慰自己,他們只是感情好扫腺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布岗照。 她就那樣靜靜地躺著,像睡著了一般笆环。 火紅的嫁衣襯著肌膚如雪攒至。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天躁劣,我揣著相機(jī)與錄音迫吐,去河邊找鬼。 笑死账忘,一個(gè)胖子當(dāng)著我的面吹牛志膀,可吹牛的內(nèi)容都是我干的熙宇。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼溉浙,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼烫止!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起戳稽,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤馆蠕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后惊奇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互躬,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年颂郎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吼渡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乓序,死狀恐怖寺酪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竭缝,我是刑警寧澤房维,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站抬纸,受9級(jí)特大地震影響咙俩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜湿故,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一阿趁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坛猪,春花似錦脖阵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至就斤,卻和暖如春悍募,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洋机。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工坠宴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绷旗。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓喜鼓,卻偏偏與公主長得像副砍,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庄岖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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