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

news/2025/10/27 20:00:49/文章来源:https://www.cnblogs.com/sun2020/p/19170014

大家好,我是 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.mzph.cn/news/948056.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

​Fedora 37 安装 libicu-71.1-2.fc37.x86_64.rpm 教程(命令行步骤)​

​Fedora 37 安装 libicu-71.1-2.fc37.x86_64.rpm 教程(命令行步骤)​​一、先确认你的系统 这个包是 ​Fedora 37、64位系统(x86_64)​​ 的,所以:你得是 ​Fedora 37​ 系统电脑是 ​64位​ 的(现在大多数都是…

十月阅读_3

“简单工具的力量” 在本章被诠释得淋漓尽致。作者以 “石头剪刀布” 的极简逻辑,隐喻编程中 “基础思维决定复杂问题解法” 的本质。我曾在一个分布式任务调度项目中,因过度依赖第三方框架的复杂配置而陷入困境;后…

学校协同云盘怎么选?2025年10大热门教育网盘推荐与对比

为满足学校在教学、科研与安全合规上的需求,选择合适的协同云盘至关重要。本文深度对比了坚果云与Zoho两款主流方案。坚果云凭借其专业的无感同步、强大的文件历史版本功能以及金融级的公安部等保三级安全认证,在效率…

从神经信号到驾驶安全:Mentalab无线脑电图系统赋能汽车人因研究与HMI优化 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

GPU集群之间的交互

目前,分散在各地的GPU智算集群之间通常没有直接的、实时的相互调用关系来共同训练一个单一的大模型。它们之间的关系更多是独立运作、资源错配或通过更高层级的调度系统进行间接协调,而不是像单个集群内部的GPU那样紧…

Java并发编程基础:从线程管理到高并发应用实践

本篇主要是多线程的基础知识,代码示例较多,有时间的可以逐个分析,具体细节都放在代码注释中了。 1. 理解线程:多任务执行的基石 1.1 什么是线程? 在现代操作系统中,进程是资源分配的基本单位,而线程是CPU调度的最…

基于ECharts 6.0实现实时材料监控看板

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

python爬取京东评论 -

python爬取京东评论使用DrissionPage模块进行爬取,DrissionPage是一个自动化的模块,可以模拟人的操作,进行翻页等操作,比传统爬取更加方便 在使用DrissionPage之前,需要先初始化路径运行下面的代码,需要将rD:\Ch…

CF1267G Game Relics

CF1267G Game Relics有 \(n\) 个物品,你可以进行下面两种操作:花费 \(c_i\) 元购买第 \(i\) 个物品。花费 \(x\) 元抽奖,等概率随机获得一个物品 \(i\)。若你已经拥有第 \(i\) 个物品,则你本次抽奖的花费改为 \(\d…

Spring Cloud Alibaba + Dubbo

Spring Cloud Alibaba + Dubbo 在微服务世界里,服务间的调用就像小伙伴之间传纸条。你可以直接跑过去,也可以用 Dubbo——微服务界的“极速传声筒”,帮你瞬间把消息传到对方手里,还保证安全可靠。 本文将从 Dubbo …

鲜花10/27

我留下了如同美梦一般的回忆/不过时间到了/最后我还想拜托你一件事/希望你可以把我忘掉

102302115方朴第一次作业

102302115方朴第一次作业 任务1代码及运行结果 import requests from bs4 import BeautifulSouptarget_url = "http://www.shanghairanking.cn/rankings/bcur/2020" res = requests.get(target_url) res.enc…

解题报告-梦熊 CSP-S2025 模拟赛T2

P14309 【MX-S8-T2】配对 题目背景 争者留其名。 题目描述 给定一个 \(n\) 个点的树,点的编号为 \(1 \sim n\),边的编号为 \(1 \sim n - 1\)。第 \(i\) 条边连接 \(u_i\) 和 \(v_i\),长度为 \(w_i\)。每个点有个 01…

读《程序员的修炼之路:从小工到专家》有感

读了《程序员的修炼之路》,深感编程远不止写代码。它提醒我,真正的专家不仅精通技术,更懂得责任与匠心。 从“小工”到“专家”,是习惯的养成,是思维从被动到主动的蜕变。这本书如同一面镜子,照见自己的不足,也…

常见问题处理 --- Invalid default value for created time

常见问题处理 --- Invalid default value for created time错误原因 在 MySQL 5.6.5 之前:只有 TIMESTAMP 类型支持 CURRENT_TIMESTAMP 作为默认值DATETIME 类型不支持 CURRENT_TIMESTAMP 默认值解决方案 方案1:使用…

鄙“站”麻将和算24,刷新后会换

鄙“站”麻将和算24,刷新后会换如题。烂代码分别如下:<pre style=display:none> <img id=tile1 src=data:image/gif;base64,R0lGODlhMgA9APcAAAAAAAAMAAwMDAEUAQAaABMTExsbGwArAAAmAAA8AAA1AB8gHyIiIiwsKy8…

20232405 2025-2026-1 《网络与系统攻防技术》实验三实验报告

20232405 2025-2026-1 《网络与系统攻防技术》实验三实验报告1. 实验内容(1)学会使用msf编码器,使用msfvenom生成jar等文件;学会veil-evasion免杀工具;学会使用C进行shellcode编程(2)掌握免杀原理、学会组合应用…

Pandas 缺失值最佳实践:用 pd.NA 解决缺失值的老大难问题

Pandas 缺失值最佳实践:用 pd.NA 解决缺失值的老大难问题 做数据处理的都知道,一个 NaN 就能让整个数据清洗流程崩盘。过滤条件失效、join 结果错乱、列类型莫名其妙变成 object——这些坑踩过的人应该都有所体会。而…

RT-Thread之事件集使用示例

事件集(Event)是 RT-Thread 中用于线程间同步的轻量级 IPC 机制,核心特性是 “多标志位” 与 “灵活触发”:通过 32 个事件标志位(bit0~bit31)表示不同事件状态,支持线程按 “逻辑与(AND)” 或 “逻辑或(OR)…