JVM系统优化实践(6):年轻代、老年代与数据计算

您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~


上回说道如果当前Survivor区中年龄相同的一批对象总大小 ≥ Survivor总数 × 50%,那么这批对象及比它们年龄更大的对象,就都直接进入老年代。但是也有可能在Minor GC之后,发现剩余的存活对象太多,导致其大小总和超过Survivor区域,那么就会把这些对象直接转移到老年代,也不计算所谓的50%。

JVM系统优化实践(6):年轻代、老年代与数据计算_第1张图片

在JDK1.6以前,老年代空间分配担保流程是这样的:

JVM系统优化实践(6):年轻代、老年代与数据计算_第2张图片

而在JDK1.6之后,它做了些微调:

JVM系统优化实践(6):年轻代、老年代与数据计算_第3张图片

老年代的垃圾回收算法和年轻代不同,对老年代触发垃圾回收的时机是:

1、在Minor GC之前:清理老年代空间;

2、在Minor GC之后:剩余对象太多会导致老年代空间不足;

而老年代的标记整理算法比年轻代回收算法慢10倍,它是这么运行的:

1、标记出老年代当前存活的对象;

2、移动对象,避免碎片化;

3、回收垃圾对象。

JVM系统优化实践(6):年轻代、老年代与数据计算_第4张图片

JDK1.6之后,-XX:HandlePromotionFailure参数已废弃,且JDK1.8无法使用。所以在知道了老年代的GC之后,如果需要做JVM性能调优,那么需要做的就是:

1、尽可能让对象在年轻代中分配和回收;

2、要极力避免年轻代频繁地进垃圾回收。

一次数据计算的案例分享:

1、一套自研的分布式数据计算系统;

2、单台机器负责100次/分钟的数据提取和计算;

3、每次提取10000条数据到内存,计算耗费10秒/次;

4、每条数据包含20个字段,平均在1K大小,10000条=10MB;

5、4C8G;

6、JVM参数:-Xms4096M -Xmn1500M;

7、年轻代和老年代的内存空间都为1500MB。

那么,这种配置下的计算任务,它多久会填满JVM?

1、年轻代为1500M(8:1:1划分,Eden区1200M,Survivor1区150M,Survivor2区150M)

2、老年代为1500M;

3、每次计算分配10M,每分钟执行100次,Eden很快被填满;

4、假设每次Minor GC时还有20个计算任务无法结束,也就是说每次有200M的空间无法回收;

5、200M > Survivor(150M),Minor GC后进入老年代;

6、每次都走担保流程,7次之后,老年代空间也会被填满;

7、如此循环往复,每隔7~8分钟就执行一次Full GC。

那么结合实际业务场景,主要问题在于每次Survivor放不下存活对象。因此优化方式也比较简单直接:

1、增加年轻代内存比例(调至2048M),老年代适度降低;

2、Full GC从7~8分钟/次,降至几小时一次,大幅提升性能;

3、还可以通过调整-XX:SurvivorRatio=8这个默认值,实现:将Eden区比例适当调低,避免通过动态判断对象年龄而进入老年代。

常见的GC垃圾回收器(型号):

1、Serial和Serial Old:分别用来回收年轻代和老年代的垃圾对象;

2、ParNew:一般用在年轻代;

3、CMS:一般用在老年代;

4、G1:统一收集年轻代和老年代。

最后说一个GC中最恐怖的现象:STW(Stop the World,类似于《斗罗》中菊鬼斗罗的「两级静止领域」)。

当Full GC时,JVM会直接停止所有系统线程,系统卡顿,不接受也不处理任何请求,直到Full GC执行完毕。这种现象,就是Stop the World。有的STW比较短,几毫秒就完了。如果在负担不重的情况下,这没什么问题。但如果在双十一的秒杀系统中发生的话,那么......

FAQ:

1、如果每次Minor GC之后,另一块Survivor区可以放下全部存活对象,那么根本不会进入老年代,也就几乎不会引起Full GC,关键问题是如何让Survivor能放下全部存活对象;

2、JVM最大内存 * N = 栈内存 + 年轻代 + 方法区 * N(N为机器数量)

栈内存 = QPS估值 * 1M * 20(倍数);

年轻代 = 30分钟(估算) * 60 * QPS估值 * 接口内存估值;

方法区 = 200M~500M。


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

你可能感兴趣的:(技术,JVM,老年代,年轻代,GC,Java)