JVM性能调优技巧
目录
设置堆内存大小及GC输出日志
GC日志文件分析
CPU过高问题定位
堆内存溢出问题定位
栈内存溢出
项目运行越来越慢原因分析
本文是基于JDK8设置的JVM参数。
-
设置堆内存大小及GC输出日志
示例说明:设置堆内存大小50M,打印GC日期,时间,GC产生的原因以及GC日志目录
-Xms50m -Xmx50m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCCause -Xloggc:D:\tmp\gclog.log
JVM参数设置可以通过在线工具:https://opts.console.heapdump.cn/ 生成,很多参数可以满足生产要求。
-
GC日志文件分析
分析上述导出的gclog.log文件,文件内容包含几类信息,如下:
1、JDK信息
2、内存信息
3、项目对应jvm参数信息
4、年轻代垃圾回收信息
其中:
2025-08-12T10:24:40.728+0800:表示垃圾回收时间,由-XX:+PrintGCDateStamps 生成
1.080:相对时间,应用启动多久触发回收,由-XX:+PrintGCTimeStamps 生成
GC (Allocation Failure) :GC原因,由-XX:+PrintGCCause 生成
PSYoungGen:不同的垃圾回收展示不同,本文用的垃圾回收是-XX:+UseParallelGC ,所以展示PSYoungGen
12800K:回收前年轻代使用的大小
2040K:回收后年轻代使用的大小
14848K:年轻代的总大小
12800K->2417K(49152K):分别表示年轻代堆内存回收前、回收后,堆内存总大小(年轻代+老年代)
0.0043385 secs:本次垃圾回收耗时
5、老年代垃圾回收信息
其中:
2025-08-12T10:24:41.880+0800:表示垃圾回收时间,由-XX:+PrintGCDateStamps 生成
2.232:相对时间,应用启动多久触发回收,由-XX:+PrintGCTimeStamps 生成
Full GC (Metadata GC Threshold):GC原因,由-XX:+PrintGCCause 生成
ParOldGen:不同的垃圾回收展示不同,本文用的垃圾回收是-XX:+UseParallelGC ,所以展示ParOldGen
10309K:回收前老年代使用的大小
7295K:回收后老年代使用的大小
34304K:老年代的总大小
11158K->7295K(46592K):分别表示老年代堆内存回收前、回收后,堆内存总大小(年轻代+老年代)
0.0372030 secs:本次垃圾回收耗时
Times: user=0.09 sys=0.02, real=0.04 secs:分别表示用户、系统、实际时间
在实际项目中,人工分析gc日志比较繁琐,借助工具分析相对容易,可以考虑通过GCeasy (https://gceasy.io/)或GCviewer分析,下方示例以GCviewer分析上述gc日志。
GC调优核心的是使得应用卡顿时间越短,即Stop the World越来越短,应用的吞吐量越大(吞吐量:应用程序所花费的时间和系统总运行时间的比值,系统总运行时间=应用程序耗时+GC耗时,应用的吞吐量一般不低于95%)。
-
CPU过高问题定位
定位步骤如下:
1、使用top命令查询到占用cpu最大的进程 top
2、查看进程内部线程运行信息 top -Hp 进程号,找到cpu占用最大的线程号
3、将线程号转换为16进制数 printf %x 线程号
4、jstack出jvm信息到文件中 jstack 1605 >cupHigh.log
5、到文件中定位问题,查看文件中包含16进制线程号的信息
下方示例:找到文件中包含646及向后50行的记录,进而定位到问题代码位置。
-
堆内存溢出问题定位
步骤图下:
1、启动项目时增加内存溢出报错后,输出dump文件
java -Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof -jar jvm-0.0.1-SNAPSHOT.jar
2、下载dump文件到本地,用jvisualvm工具分析dump文件
从示例图看出,JvmApplication的第20行heapOOM()方法抛出oom的错误
-
栈内存溢出
步骤如下:
1、通过设置-Xss大小控制每个线程的栈空间大小,如:-Xss100k
2、通过日志文件中的stackOverflowError排查异常错误,定位到程序代码位置
3、栈内存溢出很多时候是由于递归引起的,需要合理设置递归的结束条件
-
项目运行越来越慢原因分析
总结如下原因(实际情况不限于如下原因):
1、GC耗时长,导致应用吞吐量降低
可以通过分析GC日志定位问题并解决
2、JVM中代码缓存(code cache)设置不合理
CodeCache 是有限的,默认大小受 JVM 参数控制(如 -Xx:ReservedCodeCacheSize),一旦使用率过高,可能没有足够的空间来存储新生成的 JIT 编译代码。当 CodeCache 空间不足时,方法不会被编译,只能以解释模式运行,这会显著降低性能。
3、项目依赖资源越来越慢,如数据库、网络等
需要查看数据库、网络的状态分析原因及解决
4、项目线程数量过多,导致线程之间争抢激烈
通过jvisualVM工具分析Threads ,或将Thread dump出来,用fastthread(https://fastthread.io/)分析dump文件定位原因
5、服务器问题,如操作系统问题或者被其他应用进程争抢资源