当前位置: 首页 > news >正文

【性能优化必看】CPU耗时飙高?GC频繁停顿?一文教你快速定位!​

大家好,我是 Mr.Sun,一名热爱技术和分享的程序员。
​📖 个人博客​:Mr.Sun的博客
​​✨ 微信公众号​:「Java技术宇宙」
期待与你交流,让我们一起在技术道路上成长。
Mr.Sun的个人博客

一、CPU飙升

在 Java 应用中遇到 CPU 飙升问题时,排查需遵循 “从进程到线程,从现象到代码” 的递进思路,结合系统工具和 JVM 工具定位根因。以下是详细排查步骤:

1. 定位高 CPU 占用的 Java 进程

使用top命令查看进程 CPU 使用率(默认按 CPU 排序),找到 CPU 占比异常的 Java 进程,记录其PID(进程 ID)。
示例:top命令输出中,PID列对应进程 ID,%CPU列显示 CPU 使用率,若某 Java 进程(通常进程名含java或应用名)的%CPU长期高于 80%,即为目标进程。
TOP命令
2. 定位进程内高 CPU 占用的线程

Java 进程由多个线程组成,需进一步定位到具体线程。
使用top -Hp 命令查看目标进程内所有线程的 CPU 占用(-H显示线程,-p指定进程),按%CPU排序,记录TID(线程 ID)。

示例:top -Hp 12345(12345 为步骤 1 的 PID),找到%CPU最高的线程,其 TID 即为关键。
Description

3. 转换线程 ID 为十六进制

JVM 的线程堆栈信息中,线程 ID 以十六进制表示,需将步骤 2 的 TID 转换为十六进制:
使用 printf "%x\n" 命令转换。

示例:若 TID 为 12346,执行printf "%x\n" 12346得到十六进制303a(注意小写,jstack 输出为小写)。

4. 分析线程堆栈,定位代码位置

通过jstack工具获取目标进程的线程堆栈,查找高 CPU 线程的执行逻辑:

  • 执行jstack <PID> > stack.log,将堆栈信息输出到文件(避免终端滚动丢失)。
  • stack.log中搜索步骤 3 得到的十六进制线程 ID(如303a),找到对应的线程堆栈。

关键关注线程状态和执行代码:

  • 若线程状态为RUNNABLE,且堆栈显示在某个方法中持续执行(如循环、大量计算),则该方法可能是 CPU 飙升的直接原因(如死循环、复杂正则匹配、高频计算等)。
  • 若线程状态为TIMED_WAITINGWAITING,但 CPU 占比高,需结合上下文(如是否频繁唤醒 / 阻塞)。

5. 常见根因总结

  • 死循环 / 低效循环:如while(true)缺少退出条件,或循环内执行大量耗时操作(如字符串拼接、复杂计算)。
  • 频繁 GC:内存泄漏导致对象堆积,触发高频 Full GC;或新生代设置过小,导致 YGC 频繁。
  • 高频 IO / 日志:循环内大量打印日志(尤其是同步日志)、频繁读写文件 / 网络,导致 CPU 忙于 IO 处理。
  • 线程竞争:虽线程状态为BLOCKED,但大量线程竞争锁时,JVM 的锁调度会消耗 CPU。

二、JVM排查

JVM 排查是 Java 应用性能问题和稳定性问题定位的核心环节,涉及内存、GC、线程、类加载等多个维度。排查需结合监控指标、工具链和日志分析,从 “现象” 反推 “根因”。以下是系统的 JVM 排查思路,覆盖常见问题场景:

基础工具与数据收集

工具 / 命令 作用 关键参数 / 用法示例
jps 查看本地 Java 进程 PID jps -l(显示进程 PID 和主类名)
jinfo 查看 JVM 参数、系统属性 jinfo (查看所有参数);jinfo -flags (仅 JVM 启动参数)
jstat 监控 GC、类加载等统计信息 jstat -gc 1000 10(每 1 秒输出 1 次 GC 信息,共 10 次);jstat -class (类加载统计)
jmap 生成堆快照、查看堆内存分布 jmap -heap (查看堆配置与使用);jmap -dump:format=b,file=heap.hprof (生成堆 dump)
jstack 生成线程堆栈,分析线程状态 jstack > stack.log(输出线程栈到文件)
jconsole/jvisualvm 可视化监控工具(堆、线程、GC 实时数据) 直接启动jconsole,连接进程 PID 即可

场景 1:内存溢出(OOM)或内存泄漏

现象:应用抛出java.lang.OutOfMemoryError(如堆溢出、元空间溢出),或堆内存持续增长至上限。

排查步骤:

1. 确定 OOM 类型:通过错误日志判断是哪种溢出(关键)

  • Java heap space:堆内存不足(对象太多 / 太大,无法回收)。
  • Metaspace:元空间不足(类加载过多,未释放)。
  • Requested array size exceeds VM limit:数组过大,超过堆上限。

2. 收集堆数据

  • 若提前配置了-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heap.hprof,OOM 时会自动生成堆快照,直接分析即可。
  • 未配置则手动生成:jmap -dump:format=b,file=heap.hprof <PID>(生产环境注意:dump 过程可能暂停应用几秒,建议低峰期执行)。

3. 分析堆快照

使用工具:MAT(Memory Analyzer Tool)、JProfiler、VisualVM。

  • 内存泄漏定位:通过 “支配树(Dominator Tree)” 或 “泄漏嫌疑(Leak Suspects)” 报告,找到 “存活对象数异常多” 的类(如某缓存类对象占比超过 50%),检查其引用链(为何未被 GC 回收,如被静态集合强引用)。
  • 大对象定位:查看 “最大对象” 列表,若存在单个超大对象(如 100MB 的 byte []),可能是业务逻辑中不合理的对象创建(如一次性加载全量数据)。

Description

点击工具栏上的 Histogram 图标可以打开Histogram(直方图)视图,可以列出每个类产生的实例数量,以及所占用的内存大小和百分比。主界面如下图所示:

Description

图中Shallow Heap 和 Retained Heap分别表示对象自身不包含引用的大小和对象自身并包含引用的大小;

其实从图中占用内存大小排序后,基本上就能看到那个类产生了OOM,然后从代码的角度去排查一下就好了;

4. 元空间溢出补充

若为Metaspace溢出,用jmap -clstats 查看类加载统计(总类数、加载器数量),排查是否有:

  • 动态生成类过多(如反射、CGLIB 代理未限制)。
  • 类加载器泄漏(如 Web 应用热部署时,旧类加载器未被回收,导致类重复加载)。

场景 2:GC 异常(频繁 / 耗时过长)

现象: 应用卡顿(GC 暂停时间长)、CPU 飙升(GC 线程占用高),或jstat显示 YGC/FGC 次数异常(如每秒 10 次 YGC,或每分钟 3 次以上 FGC)。

1. 查看 GC 基础统计:
执行jstat -gcutil <PID> 1000,关注核心指标:

  • S0/S1:Survivor 区使用率(若长期接近 100%,可能是 Survivor 区太小,对象直接进入老年代)。
  • E:Eden 区使用率(若频繁满,YGC 频繁,可能是 Eden 区太小或对象创建太快)。
  • O:老年代使用率(若持续增长至 100%,触发 FGC,可能是内存泄漏或大对象直接进入老年代)。
  • YGC/YGCT:Young GC 次数 / 总耗时(秒)(单次 YGCT 超过 50ms 需警惕)。
  • FGC/FGCT:Full GC 次数 / 总耗时(秒)(单次 FGCT 超过 1 秒会导致应用卡顿,需紧急处理)。

Description

2. 分析 GC 日志

若未开启 GC 日志,临时配置(需重启应用,生产环境建议默认开启):

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -Xloggc:./gc.log

用工具分析日志:GCEasy(在线工具)、GCViewer(本地工具),重点看:

  • GC 原因:FGC 是否因 “老年代满”(Allocation Failure)或 “元空间满”(Metadata GC Threshold)。
  • 暂停时间:是否有单次 GC 暂停超过业务容忍阈值(如 1 秒)。
  • 对象晋升:Young GC 后晋升到老年代的对象大小(若超过老年代剩余空间,会触发 FGC)。

3. 根因判断

  • YGC 频繁:Eden 区设置过小(调大-Xmn);应用创建对象速度过快(如循环内 new 大量临时对象,优化代码复用)。
  • FGC 频繁:老年代内存泄漏(参考场景 1 排查);大对象直接进入老年代(调大-XX:PretenureSizeThreshold,让大对象先在新生代回收);GC 收集器不匹配(如 CMS 收集器在高并发下容易碎片化,可切换为 G1)。

JVM 参数调优辅助排查

很多 JVM 问题源于参数配置不合理,排查时需结合jinfo -flags <PID>检查关键参数:

  • 堆大小:-Xms(初始堆)、-Xmx(最大堆)是否过小(如小于应用实际需求),或-Xms != -Xmx导致堆频繁扩容。
  • 新生代 / 老年代比例:-XX:NewRatio(默认 2,老年代:新生代 = 2:1)、-Xmn(新生代大小)是否匹配对象生命周期(短生命周期对象多应调大新生代)。
  • GC 收集器:-XX:+UseG1GC、-XX:+UseConcMarkSweepGC等,需结合应用特点(如低延迟选 G1,高吞吐量选 Parallel)。

三、JVM参数

  • -D 可以是系统默认有的参数,也可以是自己定义的参数
-Dfile.encoding=UTF-8
-Dmaven.test.skip=true
-Dspring.profiles.active=test
-Dhsf.server.port=12404
-Dhsf.http.port=12402
  • 堆内存参数
-Xmx5M 指定最大堆内存。 如 -Xmx4g。这只是限制了 Heap 部分的最大值为4g。这个内存不包括栈内存,也不包括堆外使用的内存,默认是物理内存的1/4。
-Xms5M 指定堆内存空间的初始大小。 如 -Xms4g。 而且指定的内存大小,并不是操作系统实际分配的初始值,而是 GC 先规划好,用到才分配。 专用服务器上需要保持 –Xms 和 –Xmx 一致,否则应用刚启动可能就有好几个FullGC。当两者配置不一致时,堆内存扩容可能会导致性能抖动,默认是物理内存的1/64。
-Xmn: 设置年轻代大小,等价于 -XX:NewSize,使用 G1 垃圾收集器不应该设置该选项,在其他的某些业务场景下可以设置。官方建议设置为 -Xmx 的 1/2 ~ 1/4。
-Xss:设置每个线程栈的字节数,影响栈的深度。例如 -Xss1m 指定线程栈为1MB,与-XX:ThreadStackSize=1m 等价。
  • GC 设置参数
-XX:+UseConcMarkSweepGC:使用 CMS 垃圾回收器。
-XX:+UseSerialGC:使用串行垃圾回收器。
-XX:+UseParallelGC:使用并行垃圾回收器。
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC: jdk 11 以上开启 ZGC.
-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC:jdk 12 以上开启 ShenandoahGC。
  • 分析诊断参数
-XX:+HeapDumpOnOutOfMemoryError:当 OutOfMemoryError 产生,即内存溢出(堆内存或持久代/元空间) 时,自动 Dump 堆内存。
-XX:HeapDumpPath:与 HeapDumpOnOutOfMemoryError 搭配使用,指定内存溢出时 Dump 文件的目录。如果没有指定则默认为启动 Java 程序的工作目录。
java -Xmx5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/log/dump HeapOOM
自动 Dump 的 hprof 文件会存储到 /log/dump 目录下。

作者:Mr.Sun | 「Java技术宇宙」主理人
专注分享硬核技术干货与编程实践,让编程之路更简单。
​📖 深度文章​:个人博客「Mr.Sun的博客」 ​
🚀 最新推送​:微信公众号「Java技术宇宙

http://www.hskmm.com/?act=detail&tid=40130

相关文章:

  • 十月阅读_3
  • 学校协同云盘怎么选?2025年10大热门教育网盘推荐与对比
  • GPU集群之间的交互
  • Java并发编程基础:从线程管理到高并发应用实践
  • 102302115方朴第一次作业
  • CF1267G Game Relics
  • 中考_体育
  • python爬取京东评论 -
  • C++ STL
  • 20232422 2025-2026-1 《网络与系统攻防技术》实验三实验报告
  • 10.18 CSP-S 模拟赛
  • 20232404 2025-2026-1 《网络与系统攻防技术》实验三实验报告
  • 「WC2014-紫荆花之恋」题解
  • P14309 【MX-S8-T2】配对题解
  • 魔改sunpinyin
  • 20232308 2025-2026-1 《网络与系统攻防技术》实验三实验报告
  • [xp] GVim v9.0.494 (or thereabouts) is the last version known to support Windows XP.
  • 「CTSC2017-游戏」题解
  • 谢谢你周医生
  • 想让默认头像不再千篇一律,就顺手复刻了一下 GitHub 的思路
  • 来源未知
  • 10.27(补)
  • 袁天罡称骨歌的评骨格歌诀 - 木易
  • stm32F411RETx系列无CAN的处理思路
  • 20232402 2025-2026-1 《网络与系统攻防技术》实验三实验报告
  • Date 10.27
  • 2025年多商户商城代理招募加盟/多商户项目合伙人加盟最新推荐榜:多商户兼职项目合伙人/B2B2C商城代理招募公司/聚焦项目孵化与商户扶持能力深度解析
  • 20232420 2025-2026-1 《网络与系统攻防技术》实验三实验报告
  • 读书日记3
  • 为医疗器械行业搭建“数字桥梁”,破解协同效率与合规难题