JVM问题分析调优经验

一、前言

??JVM??性能优化步骤:

预估系统参数压测后,调整??JVM??参数线上系统监控和优化统一的??JVM??参数模板

线上频繁??Full GC??的表现:

机器CPU负载过高频繁Full GC报警系统无法处理请求或者处理过慢

频繁???Full GC??常见原因:

对象频繁进入老年代,频繁触发Full GC

系统承载高并发请求,或处理数据量过大,导致??Young GC??频繁,每次??Young GC??过后存活对象太多,内存分配不合理,??Survivor??区域过小。

系统一次性加载过多数据进入内存,大对象直接入老年代,频繁触发Full GC内存泄漏,对象无法回收,一直占用在老年代里,频繁触发Full GC??MetaSpace??(永久代)加载类过多,触发Full GC代码中使用System.gc(),触发Full GC

针对以上???Full GC??常见的原因,对应的优化方式:

??jstat??分析,合理分配内存,调大Survivor区域??dump??出内存快照,用MAT工具进行分析,代码上排查??dump??出内存快照,用MAT工具进行分析,代码上排查若内存使用不多,还频繁触发Full GC,那么优化加载的类若内存使用不多,还频繁触发Full GC,代码上排查,删除System.gc()一、案例一:高分配速率(??High Allocation Rate??)

分配速率(??Allocation rate??)表示单位时间内分配的内存量。

通常使用??MB/sec??作为单位。上一次垃圾收集之后, 与下一次??GC??开始之前的年轻代使用量, 两者的差值除以时间, 就是分配速率。分配速率过高就会严重影响程序的性能, 在??JVM??中可能会导致巨大的??GC??开销。

正常系统: 分配速率较低 ~ 回收速率 -> 健康内存泄漏: 分配速率 持续大于 回收速率 ->??OOM??性能劣化: 分配速率较高 ~ 回收速率 -> 亚健康??JVM??启动之后 291 ms, 共创建了 33,280 KB 的对象。第一次Minor GC(小型GC) 完成后, 年轻代中还有 5,088 KB 的对象存活。在启动之后 446 ms, 年轻代的使用量增加到 38,368 KB , 触发第二次GC, 完成后年轻代的使用量减少到 5,120 KB。在启动之后 829 ms, 年轻代的使用量为 71,680 KB,GC后变为 5,120 KB。

思考一个问题, 分配速率, 到底影响什么?

想一想,??new??出来的对象, 在什么地方。

答案就是,??Eden??。

假如我们增加??Eden??, 会怎么样。 考虑蓄水池效应。 最终的效果是, 影响??Minor GC??的次数和时间, 进而影响吞吐量。

在某些情况下, 只要增加年轻代的大小, 即可降低分配速率过高所造成的影响。

增加年轻代空间并不会降低分配速率, 但是会减少??GC??的频率。如果每次??GC??后只有少量对象存活,??minor GC??的暂停时间就不会明显增加。

二、案例二:过早提升(??Premature Promotion??)

提升速率(??promotion rate??)用于衡量单位时间内从年轻代提升到老年代的数据量。

一般使用??MB/sec??作为单位, 和分配速率类似。

??JVM??会将长时间存活的对象从年轻代提升到老年代。根据分代假设, 可能存在一种情况, 老年代中不仅有存活时间长的对象, 也可能有存活时间短的对象。

这就是过早提升: 对象存活时间还不够长的时候就被提升到了老年代。

??major GC??不是为频繁回收而设计的, 但??major GC??现在也要清理这些生命短暂的对象, 就会导致??GC??暂停时间过长。这会严重影响系统的吞吐量。

??GC??之前和之后的年轻代使用量以及堆内存使用量。

这样就可以通过差值算出老年代的使用量。

和分配速率一样, 提升速率也会影响??GC??暂停的频率。但分配速率主要影响??minor GC??, 而提升速率则影响??major GC??的频率。

有大量的对象提升, 自然很快将老年代填满。老年代填充的越快, 则??major GC??事件的频率就会越高。

一般来说过早提升的症状表现为以下形式:

短时间内频繁地执行??full GC??每次??full GC??后老年代的使用率都很低, 在 10-20% 或以下提升速率接近于分配速率

要演示这种情况稍微有点麻烦, 所以我们使用特殊手段, 让对象提升到老年代的年龄比默认情况小很多。 指定??GC??参数??-Xmx24m -XX:NewSize=16m -XX:MaxTenuringThreshold=1??, 运行程序之后, 可以看到下面的??GC??日志:

解决这类问题, 需要让年轻代存放得下暂存的数据, 有两种简单的方法:

增加年轻代的大小, 设置JVM启动参数, 类似这样:-Xmx64m -XX:NewSize=32m, 程序在执行时,Full GC的次数自然会减少很多, 只会对minor GC的持续时间产生影响。减少每次批处理的数量, 也能得到类似的结果。至于选用哪个方案, 要根据业务需求决定。在某些情况下, 业务逻辑不允许减少批处理的数量, 那就只能增加堆内存, 或者重新指定年轻代的大小。 如果都不可行, 就只能优化数据结构, 减少内存消耗。

但总体目标依然是一致的: 让临时数据能够在年轻代存放得下。

【文章出处:日本大带宽服务器 jap.html 欢迎留下您的宝贵建议】每一个成功者都有一个开始。勇于开始,才能找到成功的路。

JVM问题分析调优经验

相关文章:

你感兴趣的文章:

标签云: