2026-02-13 21:25:47
CPU使用率过高和JVM Old区占用过高排查过程如下:
一、报警信息确认定位高CPU进程
使用 top 命令查看占用CPU最高的进程ID(PID)。

示例结果:PID为1469的进程占用CPU最高。
定位高CPU线程
使用 top -Hp <PID> 查看该进程中占用CPU高的线程ID(TID)。

示例结果:发现两个高CPU线程,一个为打印异常日志的线程,另一个为GC线程。
转换线程ID并分析堆栈
使用 printf '%xn' <TID> 将线程ID转换为十六进制。
使用 jstack <PID> | grep <十六进制TID> -A 30 定位线程堆栈信息。
关键发现:
打印异常日志的线程频繁调用 ExceptionUtils.getFullStackTrace(e)(commons-lang包),导致大量对象创建。
GC线程因频繁Full GC(FGC)占用高CPU。


监控GC状态
使用 jstat -gcutil <PID> <时间间隔> 查看JVM内存和GC情况。

关键指标:
E(伊甸园区)和O(老年代)使用率均接近100%。
FGC(Full GC次数)频繁,说明老年代内存不足。
导出堆内存Dump文件
使用 jmap -dump:format=b,file=<文件名>.dump <PID> 导出堆内存快照。
示例命令:jmap -dump:format=b,file=heap.dump 1469
注意:Dump文件可能较大(如2.94G),需通过 sz 等工具下载到本地分析。
分析Dump文件
使用JDK自带的 jvisualvm 工具打开Dump文件,查看大对象和对象引用链。
关键发现:
大对象主要为 ElasticSearchStatusException 相关对象。
异常堆栈指向ES查询代码,进一步定位到 RestClientFactory.getRestClient().search(searchRequest) 方法。


定位ES异常原因
检查ES索引状态,发现部分索引未创建,导致ES查询抛出异常。
进一步排查发现索引创建任务已停止,需重启任务并修复数据。

直接原因:
ES索引缺失导致查询抛出 ElasticSearchStatusException。
异常日志打印代码未做流控,频繁调用 getFullStackTrace() 创建大量对象,填满老年代。
老年代内存不足触发频繁FGC,进一步加剧CPU占用。
根本原因:
缺乏对ES索引创建任务的监控和容错机制。
异常日志处理未考虑性能影响,未限制异常打印频率。
修复ES索引问题:
重启索引创建任务,确保索引正常生成。
修复丢失的索引数据,恢复ES查询功能。
优化异常日志处理:
引入流控机制(如Guava RateLimiter),限制异常日志打印频率。
避免在高频代码路径中调用 getFullStackTrace(),改用简化日志或异步记录。
JVM调优(可选):
根据业务需求调整JVM堆内存大小(如 -Xmx、-Xms)。
优化GC策略(如改用G1 GC),减少FGC对性能的影响。
通过以上步骤,可系统性解决CPU和JVM Old区占用过高问题,并预防类似故障再次发生。