调优过程
前提条件
- 服务器和操作系统正常运行。
- PC端已经安装SSH远程登录工具。
- 服务器有需要优化的Java程序且程序开启GC日志打印。
操作步骤
- 对程序进行在线分析,在GC页签下GC日志页面。
点击执行GC日志采集,获取日志并进行解析。
图1 GC日志分析 - 元空间扩容导致的GC优化。图2 优化建议
jdk8以后移除永久区,使用本地内存来存储类元数据信息并称之为元空间(Metaspace)。持续的元空间垃圾回收说明可能存在类、类加载器导致的内存泄漏或是大小设置不合适。
根据优化建议对代码进行排查,发现代码中存在每次调用循环动态创建并加载类。针对本例可减循环次数,减少类的加载;将classes对象设为单例模式避免重复调用。
图3 案例详情对于无法进行代码优化的案例结合成因分析,查看因元数据产生的gc次数及时间占比是否偏高若想降低可调高-XX:MetaspaceSize。减少因扩容造成的GC。
图4 GC成因分析 - 发生巨型对象导致的GC优化。图5 优化建议
当发生巨型对象分配时(大于region size的50%),G1会找出一个连续的可用分区集合,这样就能汇总出足够的内存来容纳巨型对象。如果没有足够的连续可用空间,G1就会启动一次full GC来压缩Java堆空间。巨对象分配后对应region不再存储其它对象造成了一定的空间浪费。通过调大-XX:G1HeapRegionSize可减少因巨对象空间不足导致的Full GC且能提升内存利用率。
图6 GC成因分析可通过GC成因分析查看G1 Humongous Allocation触发的GC占比统计看巨对象触发的GC是否由改善。
- 发生Full GC的优化。图7 优化建议
当空间即将耗尽或分配速度无法追上回收速度时就会触发Full GC,当有Full GC发生代表性能可能不足。除关注程序本身是否可优化外或提升机器性能(增加内存,提升cpu性能)外,可以尝试调整相关参数提高GC效率降低Full GC发生的可能性:
- 增加堆内存(Xmx)大小,让G1有更多的时间去完成 Concurrent Marking。
- 适当加大-XX:ConcGCThreads选项的值,增加并发标记的线程数。
- 通过增加-XX:G1ReservePercent(默认10%)的选项值,增加 G1 在 IHOP 分析过程中所需要的内存空间。
- 通过设置-XX:-G1UseAdaptiveIHOP的选项值禁用自适应IHOP分析机制,通过调小-XX:InitiatingHeapOccupancyPercent的选项(默认值为 45)值达到提前触发GC标记周期的目的。
可通过GC活动细化分析查看Full GC统计数据对比前后确定Full GC平均时间间隔是否有改善。
- 发生System.gc()导致的GC优化。图8 优化建议
程序中不建议显式调用System.gc()强制触发Full GC。建议排查代码将相关代码移除,或使用-XX:+DisableExplicitGC忽略System.gc()调用。若为系统级别或框架级别的调用无法禁用,可开启-XX:+ExplicitGCInvokesConcurrent,Full GC会变成initial-mark。
检验优化效果可对优化后程序进行日志采集查看优化建议中是否还有system.gc()的优化建议,或查看gc日志看system.gc()触发日志是否存在,若存在看是否为initial-mark阶段
总结
Java中仅根据GC日志较难定位具体问题及修改方案,可根据GC日志解析后的优化建议对代码进行排查或修改启动参数后重新启动运行收集一段时间后,可对比前后日志吞吐量、GC平均暂停时间、活动细化分析-平均时间间隔等查看GC效率是否改善。
针对GC日志进行调优需在代码层面无法做进一步优化的前提下进行,在机器性能不足以支撑业务时且根据优化建议对程序优化进行优化后无明显改善的,建议升级机器配置以适应业务需求。