☰
性能分析四件套:从死锁到CPU瓶颈全解析!
2026/10/4 11:20:40 网站建设 项目流程

全文目录:

    • 开篇语
      • 0. 前言:谁还没被性能问题折磨过?
      • 1. jstack:死锁?线程卡死?它就是你的“线程抓捕专家”!
        • 💡 场景:解决线程阻塞、死锁、CPU 飙高问题
        • 🛠️ 实战操作
        • 🔍 如何分析 `jstack.log`?
      • 2. jmap:内存泄漏?堆溢出?它就是你的“内存 X光机”!
        • 💡 场景:解决内存泄漏、OOM、GC 频繁问题
        • 🛠️ 实战操作
      • 3. jstat:GC 监控?类加载?它就是你的“JVM 仪表盘”!
        • 💡 场景:定位 GC 性能瓶颈、内存区域使用情况
        • 🛠️ 实战操作
      • 4. async-profiler:谁在偷偷吃我的 CPU?它就是你的“性能外科医生”!
        • 💡 场景:定位 CPU 瓶颈、热点方法、I/O 阻塞、内存分配热点
        • 🛠️ 实战操作
      • 5. 写在最后:工欲善其事,必先利其器!
    • 文末

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

0. 前言:谁还没被性能问题折磨过?

性能问题,对于咱们程序员来说,简直就是“魔鬼的考验”。👹 表面上看,程序运行得好好的,但一旦遇到高并发、大数据量,立马就“原形毕露”。系统响应慢、CPU 居高不下、内存飙升、莫名其妙的线程阻塞……这些都是咱们加班的元凶!

更气人的是,这种 Bug 往往在开发环境根本测不出来,只有在生产环境,在用户眼皮底下,才跳出来给你一个“惊喜”!🤡

所以,掌握性能分析工具,就如同医生掌握了诊断病症的仪器。你不能光靠经验拍脑袋,你得有数据,有证据!

今天,我就带大家领略一下jstack、jmap、jstat、async-profiler的魅力。这可不是简单的命令行教程,咱们要结合实际场景,看看它们到底能帮你解决什么鬼畜问题!👻


1. jstack:死锁?线程卡死?它就是你的“线程抓捕专家”!

jstack,全称Java Stack Trace,顾名思义,它主要用来打印 JVM 进程中所有线程的栈轨迹(stack trace)。

它的作用就像什么呢?就像你在公司年会玩“定格游戏”,突然主持人喊“Freeze!”所有人都得停下来,保持那一瞬间的动作。jstack 就是那个主持人,它能让你看到 JVM 中每个线程在某一瞬间都在干啥。🧘‍♀️

💡 场景:解决线程阻塞、死锁、CPU 飙高问题
  • 线程阻塞:某个线程卡在某个方法调用上,或者等待某个锁,导致整个请求响应慢。
  • 死锁:两个或多个线程互相持有对方所需的资源,谁也无法继续执行,永久阻塞。
  • CPU 飙高:某个线程陷入了死循环,或者在执行大量计算,导致 CPU 负载过高。
🛠️ 实战操作
  1. 找到你的 Java 进程 ID (PID):

    ps-ef|grepjava# 或者jps-l

    假设你的 PID 是12345。

  2. 生成线程堆栈:

    jstack12345>jstack.log

    注意:建议多采集几次,每次间隔几秒,比如三次:

    jstack12345>jstack1.logsleep5jstack12345>jstack2.logsleep5jstack12345>jstack3.log

    这样做的目的是,如果某个线程只是偶尔卡顿,或者在等待资源,通过多次采样就能看出它的“轨迹”。如果是死循环或者死锁,那么在多次采样中它都会停留在同一个地方。

🔍 如何分析jstack.log?
  • 看BLOCKED、WAITING、TIMED_WAITING状态:这些状态说明线程在等待。

    • BLOCKED (on object monitor):线程正在等待获取一个对象锁。
    • WAITING (on object monitor):线程调用了Object.wait()或LockSupport.park()等方法,正在等待被唤醒。
    • TIMED_WAITING:类似 WAITING,但有超时时间。
  • 寻找deadlock:jstack 会自动帮你检测死锁并高亮出来!这简直是福尔摩斯本人!✨

  • CPU 占用高的线程:结合top -Hp <PID>命令,找到 CPU 最高的线程 ID,然后将该线程 ID 转换为 16 进制,在 jstack 文件中搜索。你就能看到这个“捣蛋鬼”正在执行哪个方法!

小结:jstack 就是你的“透视眼”,能一眼看穿线程在干嘛。当程序卡顿,没有响应时,首先想到的就是它!


2. jmap:内存泄漏?堆溢出?它就是你的“内存 X光机”!

jmap,全称Java Memory Map。它主要用来生成 JVM 堆内存的快照(Heap Dump),分析内存中的对象情况。

它的作用就像什么呢?就像你拍了一张照片,把内存里所有的对象、它们之间互相引用关系都“定格”下来。然后你可以用专业的工具(如 Eclipse MAT 或 JProfiler)来分析这张照片。📸

💡 场景:解决内存泄漏、OOM、GC 频繁问题
  • 内存泄漏 (Memory Leak):对象创建后不再使用,但由于被某些引用链条“活生生”地拽着,GC 无法回收,导致内存不断上涨。
  • OOM (OutOfMemoryError):内存不足,程序崩溃。
  • GC 频繁:由于内存占用过高或分配不合理,导致 GC 频繁执行,系统卡顿。
🛠️ 实战操作
  1. 找到你的 Java 进程 ID (PID):(同 jstack)

  2. 生成堆内存快照:

    jmap-dump:format=b,file=heap.hprof12345
    • format=b:指定输出格式为二进制,这是标准的堆转储文件格式。
    • file=heap.hprof:指定输出文件名。

    注意:生成堆转储文件时,JVM 会暂停(STW),尤其是在堆内存很大的情况下,可能会暂停几十秒甚至几分钟!所以,在生产环境谨慎操作,最好选择业务低峰期。

  3. 分析堆转储文件:
    将heap.hprof文件下载到本地,用Eclipse Memory Analyzer Tool (MAT)或者JProfiler打开。

    • MAT可以帮你找到最大的对象、哪个对象占用内存最多、哪个对象是 GC Root 等等。它还有强大的“Path To GC Roots”功能,能帮你分析为什么某个对象没有被回收。这简直就是你的“内存侦探”!🔍

小结:jmap 帮你把内存挖出来,让你看到所有对象的真实面貌和关系网。是解决内存问题的终极武器!


3. jstat:GC 监控?类加载?它就是你的“JVM 仪表盘”!

jstat,全称JVM Statistics Monitoring Tool。它是一个非常轻量级的命令行工具,用来实时监控 JVM 各种运行状态信息,包括类加载、GC 行为、JIT 编译等。

它的作用就像什么呢?就像汽车的仪表盘!🚗 你可以实时看到油耗(GC 频率)、发动机转速(类加载)、车速(JIT 编译)等各种参数,而不会对汽车本身造成任何负担。

💡 场景:定位 GC 性能瓶颈、内存区域使用情况
  • GC 频繁或耗时过长:导致系统卡顿。
  • 内存区域(新生代、老年代)分配不合理:导致内存利用率低或 GC 压力大。
  • Full GC 频繁:这通常是老年代内存不足的信号。
🛠️ 实战操作
  1. 找到你的 Java 进程 ID (PID):(同 jstack)

  2. 实时监控 GC 情况:

    jstat-gcutil12345100010
    • -gcutil:显示 GC 统计信息(推荐,最常用)。
    • 12345:进程 ID。
    • 1000:每隔 1000 毫秒(1秒)刷新一次。
    • 10:总共刷新 10 次。

    输出示例:

    S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 0.00 12.56 90.23 98.76 96.54 10 0.123 1 0.045 0.168 0.00 0.00 20.34 90.23 98.76 96.54 10 0.123 1 0.045 0.168 ...

    关键字段:

    • E:Eden 区使用百分比。
    • O:Old 区(老年代)使用百分比。
    • YGC:Young GC 次数。
    • YGCT:Young GC 总耗时。
    • FGC:Full GC 次数。
    • FGCT:Full GC 总耗时。
    • GCT:所有 GC 总耗时。
  3. 监控堆内存:

    jstat-gc12345100010
    • -gc:显示堆内存使用情况(KBytes)。

小结:jstat 是你的“雷达”,能够实时发现 GC 和内存使用的异常。当你怀疑是 GC 导致卡顿,或者内存分配不合理时,jstat 是第一手资料的来源。


4. async-profiler:谁在偷偷吃我的 CPU?它就是你的“性能外科医生”!

async-profiler是这四个工具里的“新星”和“黑科技”!⭐ 它是一个低开销的、能采样的、非侵入式的 JVM 性能分析工具。它能够以非常低的开销(通常低于 1%)生成 CPU 火焰图、内存火焰图、锁火焰图等,帮助你快速定位热点代码。

它的作用就像什么呢?就像一个专业的“性能外科医生”,它能精准地切割出你的程序里最消耗 CPU、内存或者 I/O 的那块“病灶”,并用火焰图 (Flame Graph)的形式直观地展现出来。🔥

💡 场景:定位 CPU 瓶颈、热点方法、I/O 阻塞、内存分配热点
  • CPU 占用过高:找出哪个方法、哪行代码导致了 CPU 狂飙。
  • 方法调用链分析:哪个函数调用了哪个函数,最终导致了性能问题。
  • GC 频繁:找到频繁创建对象的“罪魁祸首”。
  • I/O 阻塞:分析网络 I/O、磁盘 I/O 等待耗时。
🛠️ 实战操作
  1. 下载 async-profiler:

    # 例如:gitclone https://github.com/jvm-profiling-tools/async-profiler.gitcdasync-profilermake# 编译
  2. 启动你的 Java 应用,并附加 async-profiler (或直接在运行时启动):

    # 方式一:在 JVM 启动参数中添加java-agentpath:/path/to/async-profiler/build/libasyncProfiler.so=start,file=cpu.svg,event=cpu,interval=9ms,duration=30s-jaryour_app.jar# 方式二:在运行时动态 attach (推荐,无需重启应用)# 首先找到 PID# 然后运行:./profiler.sh-d30-fcpu.svg12345# -d 30: 采样 30 秒# -f cpu.svg: 输出到 cpu.svg 文件 (会生成火焰图)# 12345: 进程 ID
  3. 分析火焰图cpu.svg:
    用浏览器打开cpu.svg文件。

    • 宽度代表耗时占比:越宽的“火焰”,说明该方法或方法栈占用 CPU 时间越多。
    • 高度代表调用栈深度:堆栈的每一层表示一个方法。
    • 顶部是“最热”方法:顶部最宽的火焰,通常就是需要优化的目标。

小结:async-profiler 及其生成的火焰图,是当前最先进、最直观的性能瓶颈分析工具。它能让你从宏观到微观,一览无余地看清程序的性能热点。简直是神器中的神器!💯

5. 写在最后:工欲善其事,必先利其器!

看到没?这四个工具,各有神通,互相配合,简直就是一套性能分析的“天团组合”!🎤

  • jstat帮你宏观监控,快速发现问题在哪里发生(GC?内存?)。
  • jstack帮你微观定位,发现是哪个线程在“磨洋工”(死锁?阻塞?)。
  • jmap帮你深层挖掘,找出是哪个对象在“吸血”(内存泄漏?OOM?)。
  • async-profiler更是直接“手起刀落”,帮你精准找到性能热点和代码瓶颈!

所以啊,兄弟们,别再做那个只会敲Ctrl+C、Ctrl+V的“CRUD 仔”了!💪 把这些工具玩溜,你就是团队里那个能“力挽狂澜”、拯救生产环境于水火之中的**“性能英雄”**!🦸‍♂️

下次再遇到性能问题,请记住我的话:“别慌,打开终端,jstack走起!”你会发现,解决性能问题,其实也挺上头的!😎

好了,今天就聊到这儿。拿起你的键盘,赶紧找个跑着的 Java 应用,实战一下这些命令吧!实践出真知!✨

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询