必须加 -l 参数才能显示线程 native id(nid),nid 是十六进制的系统线程 id,用于与 top -h 或 ps -t 的十进制线程 pid 对应;不加 -l 则仅显示 tid 和线程名,无法精准关联操作系统级线程。

用 jstack -l 查看线程 native ID(nid)
Java 堆栈里每一行线程信息都带一个 nid=0x... 字段,这就是该 Java 线程绑定的系统线程 ID(Linux 下是 pthread_t 的十六进制表示)。但这个值默认不显示——必须加 -l 参数才能看到锁信息和 nid。不加的话,jstack PID 输出里只有 tid(JVM 内部线程 ID)和线程名,没法和 top -H 或 ps -T 的结果对上。
实操建议:
-
jstack -l <pid> > thread_dump.txt</pid>保存完整快照,后续比对更可靠 - 注意
nid是十六进制,而top -H显示的是十进制;转换可用printf "%d\n" 0x2903 - 如果进程已卡死、
jstack无响应,改用jstack -F -l <pid></pid>强制抓取(会短暂暂停 JVM)
在 Linux 上用 top -H 或 ps -T 找高 CPU 线程
先定位哪个系统线程在吃 CPU,再反查它对应哪个 Java 线程。最常用的是 top -H -p <pid></pid>:开启线程视图,按 Shift+P 按 CPU% 排序,记下 top 几个线程的 PID(十进制)。
实操建议:
-
ps -T -o pid,tid,%cpu,time,cmd -p <pid></pid>更适合脚本化提取,tid列就是系统线程 ID - 别直接用
ps -ef | grep java—— 它只显示主线程,看不到子线程 - 如果看到多个线程 CPU 都很高,大概率是同一类操作(如大量
Object.wait()或Unsafe.park()轮询)
把系统线程 ID 和 Java 堆栈里的 nid 对上
把 top -H 得到的十进制线程 PID 转成十六进制,去 jstack -l 输出里搜 nid=0x...。匹配上后,就能看到它的完整堆栈、锁状态(比如 locked )、以及是否处于 WAITING、BLOCKED 或 RUNNABLE 状态。
常见错误现象:
- 转错进制:把十进制当十六进制去搜,或漏掉
0x前缀 - 时间差导致不一致:
top和jstack不是原子操作,中间线程可能已退出或状态变更;建议在问题复现时连续快速执行 - 虚拟线程不显示独立
nid:Java 21+ 的虚拟线程由 carrier thread 托管,jstack只显示 carrier 的nid,不会为每个虚拟线程分配系统线程 ID
为什么 nid 有时看起来像内存地址?
nid 在 Linux 上本质是 pthread_t 类型值,glibc 实现中它常被设为线程栈基址(如 0x00007fee9101a000),所以看起来像指针。这不是 bug,而是实现细节——JVM 用它做唯一标识,不保证可移植性。不同 OS、不同 glibc 版本下,nid 的生成逻辑可能不同。
关键点:
- 不要试图从
nid反推线程栈内容,它只是句柄 - 在信创环境(如鲲鹏+openEuler)上,
nid格式可能略有差异,优先以jstack -l实际输出为准 - 如果需要长期追踪线程生命周期,建议在代码里用
Thread.currentThread().getId()打日志,和nid交叉验证
Executors.newFixedThreadPool,又用了 Thread.ofVirtual(),此时 jstack 输出里高 CPU 的 nid 往往属于 carrier thread,真正阻塞的可能是某个虚拟线程的 IO 等待逻辑——得结合 jcmd <pid> VM.native_memory summary</pid> 和 jdk.jfr 录制才能准确定位。Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











