性能分析与剖析(Profiling & Optimization),一文带你搞懂!

举报
喵手 发表于 2026/01/15 17:10:39 2026/01/15
【摘要】 开篇语哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,...

开篇语

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

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

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

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

概览:什么问题用哪个工具(快速决策)

  • CPU 占用高 / 响应慢 → async-profiler(采样 + flamegraph),YourKit / VisualVM(采样或采样+插桩)。
  • 内存泄露 / 堆占用高 → jcmd / jmap + heap dump -> Eclipse MAT / YourKit 内存分析。
  • GC 压力 / 暂停长 → JFR(Java Flight Recorder)记录 + 分析,结合 GC 日志。
  • 锁竞争 / 线程阻塞 → async-profiler 的 lock/alloc 事件 或 YourKit 的线程/锁视图,结合 jstack
  • Micro-perf(方法级微优化) → JMH 编写基准,避免误导性结论。

一:性能剖析的标准流程(可复制的步奏)

  1. 准备

    • 确认环境(JVM 版本、启动参数、部署拓扑)。记录 baseline(响应、TPS、p50/p95/p99、CPU、heap)。
  2. 低侵入采样(快速定位)

    • 运行 async-profiler 或 JFR 取 10–60s 采样,生成 flame graph / recording。采样足够低开销可在线用。
  3. 锁/阻塞/线程分析

    • 同步分析线程堆栈(jstack / async-profiler locks)找出等待热点。
  4. 内存/泄露检查

    • 获取 heap histogram(jcmd <pid> GC.class_histogram),必要时生成 heap dump -> 用 MAT / YourKit 分析泄露路径。
  5. 精确测量

    • 针对热点代码写 JMH 基准(或小型集成基准),测量优化前后差异并控制变量。
  6. 修复→回归测试

    • 实施变更(代码/配置/调优 JVM),在同样条件下复跑采样与基准,记录改善。
  7. 持续监控

    • 用 JFR 常态化采集或 APM(必要时)做长期回归判断。

二:重要工具与典型用法(带命令示例)

注:命令示例以 Linux 环境为例,需根据你环境做小改动。

async-profiler(CPU / allocations / lock / flamegraph)

  • 下载并在目标机器上运行(需要对应内核和 JVM 符合性)。
  • 典型命令(采样 CPU 并生成 flame graph):
# 30 秒采样,输出 SVG(CPU flamegraph)
./profiler.sh -e cpu -d 30 -f /tmp/cpu.svg <pid>
# 查看火焰图,或用 -o collapsed 输出供火焰图脚本使用
./profiler.sh -e alloc -d 30 -f /tmp/alloc.svg <pid>   # 内存分配热点
./profiler.sh -e lock -d 30 -f /tmp/lock.svg <pid>    # 锁竞争采样
  • 结果可直接在浏览器中打开交互查看。async-profiler 的采样→flamegraph 工作流是定位 CPU 热点最快的方式之一。

Java Flight Recorder (JFR) + Java Mission Control (JMC)

  • 启动时添加参数(实时记录 / 运行时触发都行):
# 启动时启用 JFR(Java 11+-XX:StartFlightRecording=duration=1m,filename=/tmp/recording.jfr
# 或运行时触发(jdk9+)
jcmd <pid> JFR.start name=MyRec settings=profile duration=1m filename=/tmp/rec.jfr
  • 使用 Java Mission Control(JMC)打开 .jfr,查看方法/分配/锁/GC/线程事件,尤其擅长长时间、系统级别的诊断与 GC 分析。

jcmd / jmap / jstack(轻量现场诊断)

  • 堆直方图(注意:通常会触发 Full GC):
jcmd <pid> GC.class_histogram > /tmp/histo.txt
  • 生成 heap dump: jcmd <pid> GC.heap_dump /tmp/heap.hprof(在 MAT 中打开)
  • 打印线程栈: jcmd <pid> Thread.print > /tmp/threads.txtjstack -l <pid>jcmd GC.class_histogram 会做 Full GC(注意会影响服务)。

VisualVM / YourKit / (商业) YourKit

  • VisualVM:快速、免费,适合本地/远程简单 CPU / Memory 快照与方法级采样。
  • YourKit:商业但功能全面(线程、锁、异常、内存泄露链路),GUI 交互式分析好用,适合深度排查。

三:Flame graph、采样 vs 插桩(要点)

  • Flame graph:把调用栈按「在 CPU 上占用时间」汇总可视化,横轴是调用栈宽度(时间/样本数),纵轴是调用层级。最宽的“火焰”通常就是最值得优化的地方。
  • 采样(sampling):定时捕获线程栈。优点:低开销、可在线使用;缺点:不能得到非常精确的调用次数、短时事件可能被漏掉。
  • 插桩(instrumentation):在方法入口/出口插入计时逻辑。优点:精确(调用次数、时间);缺点:高开销(影响 JIT 优化),会改变应用行为,不适合线上长时间打开。
  • 实践:先用采样(async-profiler / JFR)定位,再在必要处用 JMH/插桩确认精确数字。

四:常见优化手段(按优先级与示例)

高优先级(大收益)

  1. 减少不必要的同步:拆细锁、使用无锁/并发集合(ConcurrentHashMap)、使用 StampedLock / ReadWriteLock
  2. 避免频繁的对象分配:复用缓冲(ThreadLocal buffer)、使用池化(谨慎使用)、避免短生命周期大量临时对象。
  3. 减少装箱/拆箱:使用原始类型数组或 primitive-specialized collections。
  4. IO 合并 / 异步化:批量写、异步请求、减少阻塞等待。

中优先级

  • 减少上下文切换:控制线程池大小、避免过度线程化。
  • 减少锁持有时间:把长耗时操作移出锁内。

低优先级(风险/小收益)

  • JVM 参数微调(如 G1/ ZGC/ Shenandoah 选择),除非 GC 分析确实指向 GC 瓶颈,否则不要盲改。

示例代码:避免装箱

// bad
Map<Integer, Long> map = new HashMap<>();
map.put(i, j); // causes autoboxing

// better (if keys small-range)
long[] arr = new long[size];
arr[i] = j;

五:性能回归测试与基准设计(必须且常被忽视)

  1. 用 JMH 写微基准:控制 JVM warmup、iteration、线程数。不要用简单 System.nanoTime() 循环。
  2. 端到端基准:对慢请求写集成 benchmark(在隔离环境),包括真实负载、网络/IO 模拟。
  3. CI 集成:把关键基准纳入 CI(非每次 PR,但重要版本里),记录基线并对回归报警。
  4. 统计显著性:多次运行取均值/分布(p95/p99),注意抖动与噪声控制(环境一致性)。

六:实战练习 — 对一个“慢请求”做完整剖析(从 0 到 1 的可执行步骤)

假设:生产环境中某 API /api/pay 响应 p95 从 200ms 跳到 1.2s。步骤:

A. 收集上下文(非侵入)

  • 获取时间区间的 metrics(CPU、heap、GC、线程数、负载)。记录 exact timestamps。
  • 在问题窗口开启 JFR(1–2 分钟)并同时运行 async-profiler 的短采样(30s)。命令示例:
# async-profiler (CPU)
./profiler.sh -e cpu -d 30 -f /tmp/pay_cpu.svg <pid>
# jcmd JFR
jcmd <pid> JFR.start name=payRec duration=2m filename=/tmp/pay.jfr

(引用:async-profiler/JFR 用法)

B. CPU 路径分析

  • 打开 pay_cpu.svg,看最宽的栈:定位到具体方法(例如 PaymentService.process() 下某个 encrypt()dbClient.query() 被点亮)。使用 async-profiler 的 -e lock 看是否为锁等候。

C. 内存 / GC 检查

  • jcmd <pid> GC.class_histogram > histo.txt 查看占实例数最多的类(是否有缓存增长、集合膨胀)。注意:该命令会触发 Full GC。
  • 若怀疑泄露:生成 heap dump jcmd <pid> GC.heap_dump /tmp/heap.hprof -> 用 MAT / YourKit 找出 GC roots 到泄露对象的路径。

D. 线程与锁

  • jstack -l <pid> > threads.txtjcmd <pid> Thread.print,寻找 BLOCKED 状态或长时间 WAITING。用 async-profiler lock 火焰图进一步确认竞争点。

E. 数据库 / 网络 IO

  • 并行监控 DB 端慢查询(慢查询日志)、网络延迟(tcpdump / pcap / APM traces)。如果 flamegraph 中显示大量时间在 socketRead / nio,优先检查网络/DB。

F. 制定修复方案(示例)

  • 若定位到 dbClient.query() 太慢:缓存热点数据(TTL、读多写少)+ 加入限流/重试背压 + 优化 SQL(索引/分片)→ 回归测试。
  • 若是 锁竞争(比如 synchronized on global map)→ 改为分段锁/ConcurrentHashMap/lock striping。
  • 若是 对象分配压力 → 复用缓冲、避免短生命周期对象、减少日志字符串拼接(使用占位符或延迟格式化)。

G. 验证

  • 用相同请求场景复跑 async-profiler / JFR / JMH(若改变了算法)并对比。务必在隔离环境重复验证 3 次以上,并记录 CPU/latency/throughput 曲线。

七:常见陷阱与调试误区(警告清单)

  • 误读采样数据:采样显示热点并不意味着「这里的代码一定慢」——采样说明“在 CPU 上的时间多”,需要结合调用上下文判断。
  • 优化非热点(Premature Optimization):优化不在火焰图上的地方通常回报很低。
  • 忽视 GC/系统层面:CPU 不一定是瓶颈(例如大量阻塞在 IO/等待),盲目优化 CPU 无效。
  • 基准写错导致误导:用 JMH 才能正确比较微优化,简单循环可能被 JIT 优化掉或受 JVM warmup 干扰。
  • 依赖单次样本:任何测量都要多次取样并看分布(均值/置信区间/p95/p99)。

八:一页执行检查表(快速操作清单)

  • [ ] 记录 baseline(时间/机器/版本/metrics)
  • [ ] 低侵入采样(async-profiler 30s + JFR 1–2m)并保存产物
  • [ ] 获取线程 dump、GC histogram、必要时 heap dump
  • [ ] 判断是否是 CPU / IO / GC / Lock 问题
  • [ ] 针对热点写 JMH 或小型集成基准进行精确测量
  • [ ] 实施改进(小步快跑)→ 回归验证 → 合入并持续监控

九:延伸阅读(官方/权威教程 — 推荐顺序)

  • Brendan Gregg — Flame Graphs(理解 flame graph 最权威入门)。
  • async-profiler 教程(示例与生成 flamegraph)。
  • Oracle / OpenJDK JFR 文档 + Java Mission Control 使用指南(系统级长期分析)。
  • jcmd 工具说明(堆直方图 / heap dump)。
  • JMH / Oracle 基准写法与陷阱(写微基准的权威指南)。

… …

文末

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

… …

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

wished for you successed !!!


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

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


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

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。