圖解JVM實驗-觸發(fā)FullGC的幾個條件

1.年輕代存活的對象太多,老年代了放不下

01.示例代碼

public class DemoTest1 {

02.啟動JVM參數(shù)

-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

其中,參數(shù)-XX:PretenureSizeThreshold,參數(shù)要設(shè)置大對象閾值為3MB晃虫,也就是超過3MB徊都,就直接進入老年代噩斟。

大對象大小是3MB。一旦對象大小超過3MB意乓,不會進入新生代烫罩,直接進入老年代惜傲。

啟動命令:

java  -jar -XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8  -XX:MaxTenuringThre

03.GC日志

啟動之后就得到如下GC日志:

Java HotSpot(TM) 64-Bit Server VM (25.151-b12) for windows-amd64 JRE (1.8.0_151-b12), built on Sep  5 2017 19:33:46 by "java_re" with MS VC++ 10.0 (VS2010)

04.分析GC日志

先看如下代碼:

 byte[] array1 = new byte[4 * 1024 * 1024];

這行代碼直接分配了一個4MB的大對象,此時這個對象會直接進入老年代贝攒,接著array1不再引用這個對象。

此時內(nèi)存分配如下:

image

緊接著就是如下代碼

byte[] array2 = new byte[2 * 1024 * 1024];

連續(xù)分配了4個數(shù)組时甚,其中3個是2MB的數(shù)組隘弊,1個是128KB的數(shù)組,如下圖所示荒适,全部會進入Eden區(qū)域中梨熙。

image

接著會執(zhí)行如下代碼:byte[] array6 = new byte[2 * 1024 * 1024];。此時還能放得下2MB的對象嗎刀诬?

不可能了咽扇,因為Eden區(qū)已經(jīng)放不下了。因此此時會直接觸發(fā)一次Young GC。

我們看下面的GC日志:

0.174: [GC (Allocation Failure) 0.174: [ParNew (promotion failed): 7457K->8328K(9216K), 0.0046949 secs]

這行日志顯示了质欲,Eden區(qū)原來是有7000多KB的對象树埠,但是回收之后發(fā)現(xiàn)一個都回收不掉,因為上述幾個數(shù)組都被變量引用了嘶伟。

所以此時怎憋,一定會直接把這些對象放入到老年代里去,但是此時老年代里已經(jīng)有一個4MB的數(shù)組了九昧,還能放的下3個2MB的數(shù)組和1個128KB的數(shù)組嗎绊袋?

明顯是不行的,此時一定會超過老年代的10MB大小铸鹰。

所以此時看gc日志:

0.179: [CMS: 8194K->6962K(10240K), 0.0033396 secs] 11553K->6962K(19456K), [Metaspace: 2970K->2970K(1056768K)], 0.0089224 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

此時執(zhí)行了CMS垃圾回收器的Full GC癌别,F(xiàn)ull GC其實就是會對老年代進行Old GC,同時一般會跟一次Young GC關(guān)聯(lián)蹋笼,還會觸發(fā)一次元數(shù)據(jù)區(qū)(永久代)的GC规个。

在CMS Full GC之前,就已經(jīng)觸發(fā)過Young GC了姓建,此時可以看到此時Young GC就已經(jīng)有了诞仓,接著就是執(zhí)行針對老年代的Old GC,也就是如下日志:

CMS: 8194K->6962K(10240K), 0.0033396 secs

這里看到老年代從8MB左右的對象占用速兔,變成了6MB左右的對象占用墅拭,這是怎么個過程呢?

很簡單涣狗,一定是在Young GC之后谍婉,先把2個2MB的數(shù)組放入了老年代,如下圖镀钓。

image

此時要繼續(xù)放1個2MB的數(shù)組和1個128KB的數(shù)組到老年代穗熬,一定會放不下,所以此時就會觸發(fā)CMS的Full GC丁溅。

然后此時就會回收掉其中的一個4MB的數(shù)組唤蔗,因為他已經(jīng)沒人引用了,如下圖所示窟赏。

image

所以再看CMS的垃圾回收日志:CMS: 8194K->6962K(10240K), 0.0033396 secs妓柜,他是從回收前的8MB變成了6MB,就是上圖所示涯穷。

最后在CMS Full GC執(zhí)行完畢之后棍掐,其實年輕代的對象都進入了老年代,此時最后一行代碼要在年輕代分配2MB的數(shù)組就可以成功了拷况,如下圖作煌。

image

05.總結(jié)

這是一個觸發(fā)老年代GC的案例掘殴,就是年輕代存活的對象太多放不下老年代了,此時就會觸發(fā)CMS的Full GC粟誓。

2.老年代可用空間小于了歷次Young GC后升入老年代的對象的平均大小

01.示例代碼

public class DemoTest1 {

02.啟動JVM參數(shù)

-XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=2M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

其中奏寨,參數(shù)-XX:PretenureSizeThreshold,參數(shù)要設(shè)置大對象閾值為2MB,也就是超過2MB努酸,就直接進入老年代服爷。

大對象大小是3MB。一旦對象大小超過3MB获诈,不會進入新生代仍源,直接進入老年代。

啟動命令:

java  -jar -XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=2M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log jvm-demo.jar

03.GC日志

啟動之后就得到如下GC日志:

老年代

年輕代

Java HotSpot(TM) 64-Bit Server VM (25.151-b12) for windows-amd64 JRE (1.8.0_151-b12), built on Sep  5 2017 19:33:46 by "java_re" with MS VC++ 10.0 (VS2010)

04.分析GC日志

(1).代碼塊1

先看如下代碼:

  byte[] array1 = new byte[1 * 1024 * 1024];

這段代碼直接分配了4個1MB的數(shù)組舔涎,并且在第4個數(shù)組的時候笼踩,會因為新生代內(nèi)存不足觸發(fā)YGC。

此時內(nèi)存分配如下:

image

對應(yīng)如下GC日志:

0.121: [GC (Allocation Failure) 0.121: [ParNew: 3155K->512K(4608K), 0.0041165 secs] 3155K->766K(9728K), 0.0042644 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此時亡嫌,可以看到新生代就只剩512K的對象嚎于,這個奇怪的512KB的對象進入Survivor From區(qū)。

那么大小為1MB的數(shù)組對象去哪里呢挟冠?肯定不是這個奇怪的512KB的對象于购。

這1MB的數(shù)組首先肯定是準備進入Survivor From區(qū),可是知染,在我們設(shè)置的JVM參數(shù)下肋僧,只有0.5MB,明顯是不夠分配的控淡。根據(jù)JVM YoungGC的規(guī)則,Survivor區(qū)放不下GC之后存活的對象嫌吠,直接進入老年代

所以掺炭,1MB的數(shù)組對象是直接進入到老年代了辫诅。

此時,內(nèi)存分配如下:

image

(2).代碼塊2

緊接這就是這塊代碼:

 array1 = new byte[1 * 1024 * 1024];

這里再次創(chuàng)建了3個1MB的數(shù)組對象涧狮,并且會觸發(fā)一次YoungGC炕矮;

對應(yīng) GC日志如下:

0.125: [GC (Allocation Failure) 0.125: [ParNew: 3663K->0K(4608K), 0.0016667 secs] 3917K->1732K(9728K), 0.0017448 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此時,Young GC之后勋篓,新生代變成0KB吧享,那么存活的大小為1MB的數(shù)組對象去哪里呢?

這1MB的數(shù)組首先肯定是準備進入Survivor From區(qū)譬嚣,可是,在我們設(shè)置的JVM參數(shù)下钞它,只有0.5MB拜银,明顯是不夠分配的殊鞭。根據(jù)JVM YoungGC的規(guī)則,Survivor區(qū)放不下GC之后存活的對象,直接進入老年代尼桶。

所以操灿,1MB的數(shù)組對象是直接進入到老年代了。

之前看到的未知的對象512KB也進入到老年代泵督,此時內(nèi)存分配如下:

image.gif

(3).代碼塊3

array3 = null;

這里再次創(chuàng)建了3個1MB的數(shù)組對象趾盐,并且會觸發(fā)一次YoungGC;

對應(yīng)的GC日志如下:

0.127: [GC (Allocation Failure) 0.127: [ParNew: 3142K->0K(4608K), 0.0013221 secs] 4875K->2756K(9728K), 0.0013592 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此時內(nèi)存分配如下:

image

(4).代碼塊4

array2 = null;

這里再次創(chuàng)建了3個1MB的數(shù)組對象小腊,并且會觸發(fā)一次YoungGC救鲤;并且在這兒,觸發(fā)Young GC之前觸發(fā)了一次CMS的Old GC,觸發(fā)的條件就是老年代可用空間小于了歷次Young GC后升入老年代的對象的平均大小秩冈。此時新生代大小變成0KB

對應(yīng)的GC日志如下:

0.129: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2756K(5120K)] 4878K(9728K), 0.0004498 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

此時內(nèi)存分配如下:

image

(5).代碼塊5

array1 = null;

此時本缠,再創(chuàng)建3個1MB的數(shù)組對象,再次觸發(fā)一次Young GC入问,執(zhí)行完YoungGC丹锹,此時新生代大小變成0KB;

對應(yīng)的GC日志如下:

0.131: [GC (Allocation Failure) 0.131: [ParNew: 3148K->0K(4608K), 0.0007974 secs] 5904K->3780K(9728K), 0.0008262 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此時內(nèi)存分配如下:

image

(6).總結(jié)

如下GC堆內(nèi)存日志我們也可以去驗證下上面的推測:

此時新生代使用了53%的大小芬失,我們還有一個1MB的數(shù)組楣黍,可能還存在一些未知對象。

在老年代中使用了大約3MB的空間棱烂,應(yīng)該就是上圖中的對象租漂。

Heap

3.幾個觸發(fā)Full GC的條件

第一:是老年代可用內(nèi)存小于新生代全部對象的大小,如果沒開啟空間擔(dān)保參數(shù)垢啼,會直接觸發(fā)Full GC窜锯,所以一般空間擔(dān)保參數(shù)都會打開;注:jDK1.8之后已經(jīng)取消了-XX:-HandlePromotionFailure機制

第二:是老年代可用內(nèi)存小于歷次新生代GC后進入老年代的平均對象大小芭析,此時會提前Full GC锚扎;

第三:是新生代Minor GC后的存活對象大于Survivor,那么就會進入老年代馁启,此時老年代內(nèi)存不足驾孔。

上述情況都會導(dǎo)致老年代Full GC。

第四:就是“-XX:CMSInitiatingOccupancyFaction”參數(shù)惯疙,

如果老年代可用內(nèi)存大于歷次新生代GC后進入老年代的對象平均大小翠勉,但是老年代已經(jīng)使用的內(nèi)存空間超過了這個參數(shù)指定的比例,也會自動觸發(fā)Full GC霉颠。默認92%

最后謝謝大家閱讀对碌,JVM之復(fù)雜,是要用頭發(fā)絲作為代價去丈量的蒿偎。向JVM沖刺朽们,離JVM近點怀读,我的頭發(fā)絲就少點。文中若有錯誤骑脱,歡迎各位指正菜枷,謝謝大家。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叁丧,一起剝皮案震驚了整個濱河市啤誊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拥娄,老刑警劉巖蚊锹,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異条舔,居然都是意外死亡枫耳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門孟抗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迁杨,“玉大人,你說我怎么就攤上這事凄硼∏π” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵摊沉,是天一觀的道長狐史。 經(jīng)常有香客問我,道長说墨,這世上最難降的妖魔是什么骏全? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮尼斧,結(jié)果婚禮上姜贡,老公的妹妹穿的比我還像新娘。我一直安慰自己棺棵,他們只是感情好楼咳,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烛恤,像睡著了一般母怜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缚柏,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天苹熏,我揣著相機與錄音,去河邊找鬼。 笑死柜裸,一個胖子當著我的面吹牛缕陕,可吹牛的內(nèi)容都是我干的粱锐。 我是一名探鬼主播疙挺,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怜浅!你這毒婦竟也來了铐然?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤恶座,失蹤者是張志新(化名)和其女友劉穎搀暑,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跨琳,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡自点,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脉让。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桂敛。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖溅潜,靈堂內(nèi)的尸體忽然破棺而出术唬,到底是詐尸還是另有隱情,我是刑警寧澤滚澜,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布粗仓,位于F島的核電站,受9級特大地震影響设捐,放射性物質(zhì)發(fā)生泄漏借浊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一萝招、第九天 我趴在偏房一處隱蔽的房頂上張望蚂斤。 院中可真熱鬧,春花似錦即寒、人聲如沸橡淆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逸爵。三九已至,卻和暖如春凹嘲,著一層夾襖步出監(jiān)牢的瞬間师倔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工周蹭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趋艘,地道東北人疲恢。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像瓷胧,于是被迫代替她去往敵國和親显拳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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

  • 作者:一字馬胡 轉(zhuǎn)載標志 【2017-11-12】 更新日志 日期更新內(nèi)容備注 2017-11-12新建文章初版 ...
    beneke閱讀 2,184評論 0 7
  • JVM架構(gòu) 當一個程序啟動之前搓萧,它的class會被類裝載器裝入方法區(qū)(Permanent區(qū))杂数,執(zhí)行引擎讀取方法區(qū)的...
    cocohaifang閱讀 1,646評論 0 7
  • 0. 前言 JVM筆記系列,以JDK1.7為基準瘸洛,主要以《深入理解Java虛擬機》(第二版)和《Java虛擬機規(guī)范...
    郭尋撫閱讀 893評論 0 3
  • Java 虛擬機有自己完善的硬件架構(gòu), 如處理器揍移、堆棧、寄存器等反肋,還具有相應(yīng)的指令系統(tǒng)那伐。JVM 屏蔽了與具體操作系...
    尹小凱閱讀 1,678評論 0 10
  • 真空的自己在和別人交流的過程中,沒有達到我預(yù)期的結(jié)果石蔗。反而陷入了自我懷疑中罕邀。我真的不能和周圍的人相處嗎,那樣我的生...
    法桐閱讀 175評論 0 2