一次JVM调优

         在某应用的性能测试时,当并发增加一定程度TPS出现下降且波动很大,但系统并没有出现明显的资源使用瓶颈。查看GC日志发现5min内Full GC次数达到43次,平均FullGC时间为1s。

一次JVM调优 - 网易杭州QA - 网易杭州 QA Team

 

         为了定位问题,增加-XX:+PrintTenuringDistribution参数输出对象age的大小分布 使用ParNew收集器时才会生效) ,同时使用VisualVM工具观察其垃圾回收的过程。可以看出,Young区的对象很快进入了Old区,首先想到的解决方法是增加Young区大小,使得对象在Young区存活时间更长,尽量通过YoungGC回收掉。因此将原来4G的JVM内存增加到8G,在相同的并发下,Full GC 次数减少到6次,平均Full GC时间1s,性能有所好转。

一次JVM调优 - 网易杭州QA - 网易杭州 QA Team

         将JVM增加到8G后,观察到Young区对象的Tenuring Threshold仍然为1,且这些对象并没有达到Max Tenuring Threshold(默认是15)就晋升到了老年代。

一次JVM调优 - 网易杭州QA - 网易杭州 QA Team

通过复习JVM的内存分配原则,才恍然大悟。其中一条是这样的:

  • 动态对象年龄判定

为了能更好地适应不同程序的内存状况,虚拟机并不总是要求对象的年龄必须达到Max Tenuring Threshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到Max Tenuring Threshold中要求的年龄。

有了理论依据,这下调节起来就有的放矢了。在保持JVM内存8G不变的情况下,同时增加参数-XX:SurvivorRatio=2,将survivor区大小为1.5G左右,再通过VisualVM查看Tenuring Threshold=1的对象所占比例已远远减小,如下图所示,这就避免了Young区对象过早进入Old区。

一次JVM调优 - 网易杭州QA - 网易杭州 QA Team

         分析GC日志得到Full GC次数为0,Young GC次数为102次,平均YoungGC时间为0.1S,TPS增加明显,且波动较小,性能提升明显。不同JVM参数配置下的性能表现如下:

 JVM参数 Full GC次 Full GC Time Young GC次数 Young GC Time TPS
 JVM 4g

Young区2g

Survivor区 256M

Old区2g

 43 0.7s 220 0.07s 631
 JVM 8g

Young区2g

Survivor区 512M

Old区5g

 6 1s 118 0.08s673 
 JVM 8g

Young区3g

Survivor区 1.5g

Old区2g

 0 - 102 0.1s 808

最后在回顾下JVM的内存分配原则和分代GC原则,理解而熟记以便不时之需。

JVM内存分配原则

  • 对象优先在Eden分配

Eden空间不足时发起一次Minor GC, -XX:+PrintGCDetails这个收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志

  • 大对象直接进入老年代

虚拟机提供了-XX:PretenureSizeThreshold,大于这个设置值的对象直接在老年代中分配 。只对Serial和ParNew两款收集器有效,Parallel Scavenge收集器不认识这个参数

  • 长期存活的对象将进入老年代

对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度 (默认为15)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过 参数-XX:MaxTenuringThreshold来设置。

  • 动态对象年龄判定

如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

JVM分代垃圾回收原则

MinorGC

  • 当创建对象时Eden区剩余空间不足,触发MinorGC,进行年轻代( Eden 、1个survivor区)垃圾回收。
  • 默认情况下Full GC会触发Minor GC,在PS GC时可通过设置-XX:-ScavengeBeforeFullGC来禁止触发Minor GC。

OldGC

  • 老生代空间不足 。
  • 永久代Permanet Generation空间满 。
  • 晋升失败 。Promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成
  • 空间分配担保 。每次Minor GC时,JVM会计算Survivor区移至老年区的对象平均大小,如果这个值大于老年区的剩余空间则进行一次Full GC,否则将检查HandlePromotionFailure设置,若设为true,则只进行MinorGC,反之进行Full GC。
  • System.gc()被显式调用

最后的最后,JVM理论一定要结合实践和业务对象的特点,没有最优的参数,只有适合业务对象特点的相对最优参数。对于JVM参数调优的一点小心得:

首先,对于可能会有GC问题的应用,尽可能详细地收集JVM GC的信息,GC日志分析和VisualVM工具搭配使用 。

其次,结合业务对象的特点和JVM内存分配原则和GC原则,找准问题所在。

最后,使用合适的JVM参数进行调节,验证其效果。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值