← 返回文章列表
产品动态 3 分钟阅读 炬鲸团队

持续剖析:用火焰图把 CPU/内存热点定位到具体函数

指标告诉你服务慢了,火焰图告诉你慢在哪一行。本文介绍炬鲸 OBSERVE 持续剖析:火焰图读法、低开销采样配置,以及 CPU、内存、锁竞争三个排障场景。

为什么光有指标和链路还不够

CPU 冲到 90%,接口 P99 从 50ms 涨到 300ms。指标告诉你"出事了",链路告诉你"慢在 order-service 这个服务",但要回答"到底哪段代码在吃 CPU、哪个对象在占内存",还得靠剖析(Profiling)。

老办法是出问题时临时挂一次 pprof 或 async-profiler,但等你连上去,流量峰值往往已经过去,抓不到现场。炬鲸 OBSERVE 的持续剖析把"临时抓包"变成"一直录着":默认每 60 秒采一次、每次采样约 10 秒,以 3% 以内的 CPU 开销持续记录各进程调用栈。出问题时可以直接回看过去 15 分钟甚至更长时间的栈快照,不用复现。

火焰图怎么读

火焰图横轴代表采样占比,纵轴是调用栈,宽度越宽说明占用越高。看的时候按这个顺序:

  1. 先看整体,找 CPU 占用最宽的那几块"平顶"(plateau);
  2. 点进去下钻,一路到具体函数;
  3. 拿异常时段和正常时段的火焰图做 diff,看哪些函数宽度明显变宽。

一个常见误区是只看总宽度。真正该优化的是"自耗时"(self time)——函数自身代码的耗时,不含它调用的子函数。有些函数自己很窄,但被几万个调用点反复调用,加起来也很可观,这类要用"调用者视角"才能找出来。

接入与采样配置

在目标主机装一个采集代理,给进程打上标签即可,不用改业务代码。典型配置:

profiling:
  enabled: true
  interval: 60s
  duration: 10s
  targets:
    - service: order-service
      process: java
      lang: jvm
      cpu: true
      alloc: true

JVM 服务建议同时开 CPU 和内存分配(alloc)两项;Go 服务用 pprof 的 CPU 与 heap;Node.js 重点看 event loop 阻塞与 GC。采样开销控制在 3% 以内,生产可以直接长开;也可以配置低峰期自动调高采样频率、高峰期自动调低。

三个排障场景

  • CPU 打满:火焰图定位到一段正则与序列化逻辑,改成预编译和对象复用后,CPU 从 90% 回落到 30%。
  • 内存缓慢增长:对比两个时间点的堆火焰图,发现一个缓存 Map 只增不减,加 TTL 后内存曲线走平。
  • 锁竞争:锁等待占比高,说明多线程在抢同一把锁,改成分段锁后吞吐翻倍。

建议把剖析和告警联动:CPU 或内存指标触发告警时,自动截取告警前后各 5 分钟的火焰图快照,省去人工复现,也方便事后复盘。