作者簡介:大家好,我是smart哥,前中興通訊、美團(tuán)架構(gòu)師,現(xiàn)某互聯(lián)網(wǎng)公司CTO
聯(lián)系qq:184480602,加我進(jìn)群,大家一起學(xué)習(xí),一起進(jìn)步,一起對抗互聯(lián)網(wǎng)寒冬
學(xué)習(xí)必須往深處挖,挖的越深,基礎(chǔ)越扎實(shí)!
階段1、深入多線程
階段2、深入多線程設(shè)計(jì)模式
階段3、深入juc源碼解析
階段4、深入jdk其余源碼解析
階段5、深入jvm源碼解析
一、簡介
上一章,我們已經(jīng)進(jìn)行了一次對象晉升的模擬,本章我們將繼續(xù)結(jié)合代碼示例做實(shí)驗(yàn),來看看老年代的GC是如何觸發(fā)的。
1.1 JVM內(nèi)存參數(shù)
我們的示例程序基于JDK1.8,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
上述給新生代分配了10MB空間,老年代也是10MB,參數(shù)注意一點(diǎn):
- -XX:PretenureSizeThreshold=3145728:超過3MB的大對象直接進(jìn)入老年代
二、示例程序
2.1 程序源碼
示例程序代碼如下:
public class Demo1 {
public static void main(String[] args) {
byte[] array1 = new byte[4 * 1024 * 1024];
array1 = null;
byte[] array2 = new byte[2 * 1024 * 1024];
byte[] array3 = new byte[2 * 1024 * 1024];
byte[] array4 = new byte[2 * 1024 * 1024];
byte[] array5 = new byte[128 * 1024];
byte[] array6 = new byte[2 * 1024 * 1024];
}
}
2.2 JVM內(nèi)存模型
我們根據(jù)上述代碼來分析下內(nèi)存中的對象分配。首先創(chuàng)建4MB的數(shù)組對象,由于超過大對象閾值3MB,所以直接進(jìn)入老年代:
接著array1失去引用,然后在Eden區(qū)分配3個(gè)2MB數(shù)組和1個(gè)128KB數(shù)組:
然后,執(zhí)行代碼byte[] array6 = new byte[2 * 1024 * 1024]
,希望在Eden區(qū)繼續(xù)創(chuàng)建一個(gè)2MB的數(shù)組。顯然,Eden區(qū)的空間不足了,此時(shí)即將觸發(fā)Young GC。
2.3 程序執(zhí)行
我們執(zhí)行程序,得到以下GC日志:
0.260: [GC (Allocation Failure) 0.261: [ParNew (promotion failed): 8142K->8797K(9216K), 0.0035404 secs]0.264: [CMS: 8194K->6772K(10240K), 0.0064863 secs] 12238K->6772K(19456K), [Metaspace: 3227K->3227K(1056768K)], 0.0103195 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
0.272: [GC (CMS Initial Mark) [1 CMS-initial-mark: 6772K(10240K)] 9112K(19456K), 0.0004078 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.273: [CMS-concurrent-mark-start]
0.274: [CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.274: [CMS-concurrent-preclean-start]
0.274: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.274: [CMS-concurrent-abortable-preclean-start]
Heap
par new generation total 9216K, used 2422K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 29% used [0x00000000fec00000, 0x00000000fee5d898, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
concurrent mark-sweep generation total 10240K, used 6772K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 3233K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 350K, capacity 388K, committed 512K, reserved 1048576K
三、日志分析
我們先來看下日志中的下面這行,這是本輪GC情況的概要說明:
0.260: [GC (Allocation Failure) 0.261: [ParNew (promotion failed): 8142K->8797K(9216K), 0.0035404 secs]0.264: [CMS: 8194K->6772K(10240K), 0.0064863 secs] 12238K->6772K(19456K), [Metaspace: 3227K->3227K(1056768K)], 0.0103195 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
ParNew (promotion failed): 8142K->8797K(9216K), 0.0035404 secs:?:這行表明ParNew首先進(jìn)行了一次Young GC,但是發(fā)現(xiàn)Eden區(qū)內(nèi)的對象全部被引用著,一個(gè)都回收不掉。于是會(huì)嘗試將存活對象轉(zhuǎn)移到Survivor,是Survivor空間不足,所以又會(huì)嘗試轉(zhuǎn)移到老年代,但是老年代可用空間也只有6MB,容納不了3個(gè)2MB和1個(gè)128KB數(shù)組,所以就會(huì)觸發(fā)下面的Full GC。
[CMS: 8194K->6772K(10240K), 0.0064863 secs] 12238K->6772K(19456K), [Metaspace: 3227K->3227K(1056768K)], 0.0103195 secs:?從這里可以看到,CMS垃圾回收器執(zhí)行了Full GC,F(xiàn)ull GC會(huì)對老年代進(jìn)行Old GC,并是和上面的Young GC關(guān)聯(lián)的,同時(shí)還會(huì)對元數(shù)據(jù)區(qū)(永久代)進(jìn)行回收。
因?yàn)?,老年代雖然容納不了全部3個(gè)2MB數(shù)組和1個(gè)128KB數(shù)組,但是可以容納2個(gè)2MB數(shù)組,所以會(huì)先將這兩個(gè)2MB數(shù)組對象轉(zhuǎn)移到老年代:
然后,發(fā)現(xiàn)剩下的一個(gè)2MB數(shù)組和128KB數(shù)組實(shí)在放不下了,就會(huì)進(jìn)行一次Old GC([CMS: 8194K->6772K(10240K), 0.0064863 secs] ),可以看到老年代空間最終變成了6772KB。
因?yàn)镃MS進(jìn)行Old GC時(shí)先對老年代清理,回收掉沒有引用的那個(gè)4MB數(shù)組,然后將新生代中的2MB數(shù)組和128KB數(shù)組轉(zhuǎn)移到老年代:
最后,F(xiàn)ull GC完畢后,byte[] array6 = new byte[2 * 1024 * 1024]
這行代碼對應(yīng)的2MB數(shù)組被成功分配到Eden區(qū):
文章來源:http://www.zghlxwxcb.cn/news/detail-801470.html
四、總結(jié)
本章通過GC日志分析了一個(gè)觸發(fā)老年代GC的案例,即新生代存活對象太多,放不進(jìn)Survivor區(qū),同時(shí)也放不進(jìn)老年代,此時(shí)就會(huì)觸發(fā)CMS的Full GC。
當(dāng)然,觸發(fā)老年代GC的另一種情況就是:當(dāng)老年代內(nèi)存占用達(dá)到一定的比例,通過?-XX:CMSInitiatingOccupancyFaction
參數(shù)可以設(shè)置這個(gè)比例,JDK1.6中默認(rèn)是92%。文章來源地址http://www.zghlxwxcb.cn/news/detail-801470.html
到了這里,關(guān)于JVM實(shí)戰(zhàn)(18)——模擬Full GC的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!