能定位到具体代码行,但必须经过“进程→线程→栈帧”三级穿透,漏掉任何一环就卡在方法名,看不到行号。

能定位到具体代码行,但必须经过“进程→线程→栈帧”三级穿透,漏掉任何一环就卡在方法名,看不到行号。
top -Hp 找不到高 CPU 线程?检查是否开了线程显示模式
很多同学执行 top -Hp <pid></pid> 后看到的全是 0% CPU,根本找不到热点线程。这不是命令错了,而是 top 默认不实时刷新线程视图——它需要你先按 H 键(大写)切换到线程模式,再按 P 按 CPU 排序。直接输 top -Hp <pid></pid> 只是启动时进入线程视图,后续仍需手动按 P 才生效。
- 确认终端支持 ANSI 控制符(某些跳板机或老旧 SSH 客户端会禁用)
- 如果
top -Hp <pid></pid>输出为空,试试ps -mp <pid> -o tid,%cpu,time | sort -k2 -nr | head -10</pid>替代 -
%CPU列显示的是该线程在采样周期内占用单个逻辑核的百分比,100% 表示占满一个核;Java 进程总 CPU 是所有线程之和,所以 4 核机器上 Java 进程 CPU 达 380% 是完全可能的
jstack 日志里搜不到 0x 开头的线程 ID?注意大小写和前缀
jstack 输出中线程 ID 格式固定为 0x,比如 0x4d2。但你用 printf "%x\n" 1234 得到的是 4d2(无前缀、小写),直接 grep "4d2" 会失败——因为日志里是 0x4d2,且可能混在 tid=0x4d2 或 java.lang.Thread.State: RUNNABLE 上方几行。
- 务必加
0x前缀搜索:grep -A 20 "0x4d2" jstack.log - 别用
grep -i:虽然0X4D2理论上存在,但 JVM 实际输出永远是小写0x - 如果匹配结果为空,检查
jstack.log是否完整:有些容器环境因权限限制,jstack可能只输出部分线程(提示Unable to get thread dump),此时换kill -3 <pid></pid>触发堆栈到 stdout 或日志文件更可靠
找到线程栈却看不到 Java 行号?确认 class 文件带调试信息
即使成功定位到某线程的 RUNNABLE 栈帧,也可能只看到 com.example.service.Task.run(),没有 Task.java:88 这样的行号。这不是排查流程问题,而是编译产物缺失调试符号。
- 生产 JAR 包若用
maven-compiler-plugin默认配置构建,且未显式开启debug,JVM 就无法映射字节码到源码行 - 验证方式:解压 JAR,用
javap -l <class></class>查看是否有LineNumberTable属性 - 补救办法有限:重启应用时加 JVM 参数
-XX:+UseSplitVerifier(仅旧版 JDK)无效;真正可行的是重建带-g的包,或用arthas的jad命令反编译(依赖运行时类加载器,不一定能还原原始行号)
Arthas 的 thread -n 3 为什么有时不准?它统计的是采样窗口内的平均值
arthas 的 thread -n 3 看起来最省事,但它底层调用的是 JVM TI 的 GetThreadCpuTime,返回的是自线程启动以来的累计 CPU 时间,再除以采样间隔算出“平均占用率”。这意味着:
- 刚启动的短生命周期线程(如定时任务触发的 Runnable)可能因时间太短被忽略
- 如果高 CPU 是脉冲式的(比如每分钟打一次 Full GC + 大量对象分配),而
thread命令采样间隔是 10 秒,就很可能错过峰值 - 它不等价于
top -Hp的实时调度器视角,两者数值常有 10%–30% 差异
真要抓瞬时热点,得用 async-profiler 生成火焰图:./profiler.sh -e cpu -d 30 -f /tmp/flame.html <pid></pid>,它基于 perf_events,采样精度到微秒级,且能下钻到 native 方法和 JIT 编译后的汇编行。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











