调优背景:

项目运行卡顿,页面加载慢。

排查问题思路:

查看CPU使用率,内存使用率,分析是否有耗时的线程,是否由于编码不规范导致产生对象过多等。

 

使用’top’命令,查看cpu使用率(%CPU),内存使用率(%MEM),PID进程ID

java响应过慢 应用响应慢 jvm_JVM性能调优

使用’top -Hp pid’命令,查看进程中线程耗费CPU的时间

java响应过慢 应用响应慢 jvm_java响应过慢_02

使用’printf “%x\n” 线程id’命令,得到线程id的十六进制值

java响应过慢 应用响应慢 jvm_jstack_03

java响应过慢 应用响应慢 jvm_java响应过慢_04

使用’jstack 进程id | grep 线程id的十六进制值’命令查看进程的堆栈信息

java响应过慢 应用响应慢 jvm_jmap_05

可以看到,是垃圾回收线程cpu使用率较高,说明一直在进行垃圾回收

使用’jmap -heap pid’再来看一下堆内存的具体使用情况

java响应过慢 应用响应慢 jvm_jmap_06

老年代基本被占满,所以导致了频繁FGC。但是未发生内存溢出错误,猜测是项目本身需要初始化对象占用的空间较大。

使用’jmap -histo:live pid | more’命令,查看内存中的存活对象情况

java响应过慢 应用响应慢 jvm_JVM性能调优_07

可以看到字节数组占用空间最大(约113MB)

猜测是下面的map中存放了很多String和字节数组,需要熟悉项目架构再具体分析。

使用’jmap -dump:format=b,file=/目录/文件名.dat pid’命令导出dump文件。

使用jvisualvm(jdk的bin目录下)作进一步分析。(若dump文件过大,不建议生成dump文件,导出过程会影响程序运行)
 

java响应过慢 应用响应慢 jvm_垃圾回收_08

java响应过慢 应用响应慢 jvm_java响应过慢_09

从上至下是对象占用空间的排序,

需要进一步熟悉业务代码,哪里有使用了较多的字节数组,字符串,集合

java响应过慢 应用响应慢 jvm_java响应过慢_10

发现有循环引用,可能是导致无法被垃圾回收器回收掉的原因?

使用’jstat -gc pid 采样时间间隔 采样数量’命令,查看垃圾回收信息

java响应过慢 应用响应慢 jvm_java响应过慢_11

可以看到平均不到2s就要触发一次FULL GC,并且每次GC后,堆的已使用空间并未明显减少,说明存货对象较多,同时,也未见有明显增长,说明是项目本身运行需要初始化约456.8MB的对象空间

调整堆空间内存为2GB后,再次查看垃圾回收信息

如图,间隔60s采样,采样十次

java响应过慢 应用响应慢 jvm_java响应过慢_12

总结:

老年代空间使用增长依然略快,建议按照上面分析优化代码。

堆空间内存调整为2G后,基本可以平稳运行,建议线上调整为4G左右。或调整年轻代和老年代比率,提升年轻代空间,减少YGC。