您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~
在JDK8及其之前,一直用的都是ParNew+CMS的组合:ParNew负责年轻代的垃圾回收,而由CMS负责老年代的垃圾回收,但会产生Stop the World这个无法避免的问题。
所以为了解决这个问题,出现了G1垃圾回收算法。G1不再区分年轻代和老年代,它可以同时回收年轻代和老年代。总的来说,G1的特点是:将JVM堆拆分为多个大小相等Region。物理上不再划分年轻代和老年代,但逻辑上仍然存在。可以设置垃圾回收的预期停顿时间,例如:1小时内STW时间不能超过1分钟。
G1的核心设计思路是:
1、追踪每个Region中的回收价值,例如:Region中有多少垃圾?又需要花费多少时间回收?
2、得到预期卡顿时间与已卡顿时间的差值,对每个Region的“回收价值”进行估算;
3、在尽可能短的时间内回收尽可能多的垃圾。
并且如果之前的GC已经造成了系统卡顿,当又要进行GC的时候,就会评估回收哪个Region中的垃圾更合理。很明显:花费1s回收10M垃圾,肯定不如花费200ms回收20M垃圾性价比高。
所有Region的内容都由G1分配,同一个Region,要么是年轻代,要么是老年代。而且同一个Region,不能既是年轻代,又是老年代。所以年轻代区域和老年代区域是动态变动的,没有固定的大小和范围。
可以通过JVM参数-XX:+UseG1GC来启用G1。
1、单个Region大小 = JVM堆大小 / 2048(最大值,且为默认值)
2、单个Region大小必须是2的指数倍数,如2M,4M,8M等......,因为计算机是二进制,这样便于处理;
3、可以使用-XX:G1HeapRegionSize=8M调整单个Region大小;
4、默认年轻代= JVM堆 × 5%,可使用-XX:G1NewSizePercent=10来调节;
5、年轻代占比默认60%,可使用-XX:G1MaxNewSizePercent=10来调节。
虽然JVM已经从ParNew+CMS切换到了G1,但-XXSurvivorRatio=8仍然有用。例如100个Region,那么如果80个是Eden区,那么S0、S1会各占10个。
G1NewSizePercent和G1MaxNewSizePercent为实验属性,需要通过-XX:+UnlockExperimentalVMOptions打开。当年轻代所用Region空间之和 ≥ -XX:G1MaxNewSizePercent设置的值时,就会触发G1的年轻代GC。年轻代的GC仍然采用的是古老的复制算法。
-XX:MaxGCPauseMillis=200指定系统卡顿时间(默认200ms),超过这个时间,无论垃圾回收是否终止,都会强行结束。
G1追踪每个Region的回收价值的方式,其实和ParNew类似:年轻代“躲过”-XX:MaxTenuringThreshold指定的次数。
G1的动态年龄判定规则:年轻代GC后,全部存活对象大小总和超过Survivor × 50%,则年龄最大的一批存活对象全部进入老年代。也就是有一批Region的属性由年轻代切换为老年代。
对于大对象的处理,G1提供了专门的Region存放大对象:
对象大小>单个Region大小 × 50%,就是大对象。如果单个Region放不下,可能会横跨多个Region存放,GC时,也会连带大对象一起进行回收。
G1的特点就先说这么,后面继续说G1的混合回收。
感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~