用 top -Hp PID 并按 P 排序定位高 CPU 线程,将 TID 转十六进制后在 jstack 日志中搜索 RUNNABLE 栈帧,即可精准定位 Java 热点代码行。

top 命令怎么快速揪出“真凶”进程?
top 是第一反应工具,但默认视图容易误判:它按进程(PID)排序,而真正吃 CPU 的往往是某个线程(TID),不是整个进程。直接看 %CPU 列最高值,再按大写 P 键强制按 CPU 使用率降序——这是关键动作,否则可能被平均值蒙蔽。
- 如果
%Cpu(s)行中us(user)占比超 80%,大概率是应用层问题(如 Java 死循环、Python 无限while True) - 如果
sy(system)高,说明频繁陷入内核,可能是锁竞争、大量epoll_wait或clone系统调用 - 如果
wa(iowait)明显,别急着查 CPU,先用iostat -x 1看磁盘是否卡住
注意:top 默认刷新间隔是 3 秒,对瞬时尖峰不敏感;可输 1 改为 1 秒刷新,避免漏掉短时爆发。
如何从进程 PID 快速定位到 Java 线程和代码行?
找到高 CPU 进程(比如 PID=12345)后,下一步必须下钻到线程级,因为 JVM 里一个 java 进程常有上百线程,真正“作恶”的往往只有一个。
- 先用
top -Hp 12345查看该进程所有线程,同样按P排序,记下最高%CPU对应的 TID(例如 12399) - 立即转十六进制:
printf "%x\n" 12399→ 得到306f(注意:Javajstack输出里的 nid 就是这个格式,形如nid=0x306f) - 导出堆栈:
jstack 12345 > stack.log,然后搜索0x306f,重点看状态为RUNNABLE的栈帧,通常第 1–3 行就是热点方法
常见坑:
- 直接在
jstack输出里搜十进制 TID(比如搜12399)一定找不到——JVM 日志只存 hex -
top -H显示的 PID 列其实是 TID,Linux 内核中线程和进程共享 PID 命名空间,TID = PID,这点别混淆
strace 和 perf 怎么选?什么场景该用哪个?
strace 和 perf 都能深挖系统行为,但目标完全不同:
本书以培养高级网站建设与管理人才为目标,内容循序渐进,由浅入深,通过大量的实例系统全面地介绍了Linux+PHP+MySQL环境下的网络后台开发技术。本书详尽分析了近30个典型案例。包括计数器、网站流量统计、留言板、论坛系统、聊天室、投票与调查、用户管理、新闻发布系统、广告轮播、购物系统等等,力求让读者通过对案例的学习,轻松掌握PHP和MySQL的编程精要,迅速掌握网络后台开发技巧。 本书适
- 用
strace -p是为了确认“它到底在反复调什么系统调用”。比如看到满屏clock_gettime(CLOCK_MONOTONIC, ...)或高频write(1, ...),基本锁定是日志刷屏或时间戳滥用。 - 用
perf top -p是为了看“CPU 时间花在哪条指令/函数上”。输出里如果java::String::equals或PyUnicode_Compare占比异常高,说明字符串比较逻辑有问题;如果是memset或memcpy高,则可能是大对象频繁复制。
注意:
-
strace开销极大,线上慎用;perf相对轻量,但需确保内核开启perf_event_paranoid(cat /proc/sys/kernel/perf_event_paranoid≤ 2) -
perf record -p+-g -- sleep 10 perf script可生成火焰图,比perf top更利于发现调用链路中的隐藏瓶颈
为什么 load average 高 ≠ CPU 忙?别被数字骗了
load average(如 load average: 4.15, 3.80, 3.20)反映的是“等待 CPU 或不可中断 I/O 的任务数”,不是 CPU 使用率。
- 在 4 核机器上,load=4.15 并不意味着 CPU 100%,它可能只是 2 个进程在等磁盘读,另 2 个在等网络响应,此时
%Cpu(s)的us和sy可能都不到 30% - 真正危险的组合是:load 高 +
wa高 → 磁盘瓶颈;load 高 +us高 → 应用计算瓶颈;load 高 +sy高 +cs(context switch)超高 → 锁或调度风暴
所以,看到 load 报警,第一反应不该是杀进程,而是跑一遍:vmstat 1 5(看 cs 和 bi/bo)、pidstat -t -p (看线程级上下文切换)、iotop(看谁在狂写磁盘)
最易忽略的一点:容器环境里,top 显示的 PID 是宿主机视角的,而 jstack 或 strace 必须进容器 namespace 才能拿到真实线程栈——别在宿主机上对容器内 Java 进程直接 jstack,那会失败或返回空。









