先定位高CPU的Java进程和线程,再通过TID转十六进制匹配jstack堆栈:top -Hp 找高CPU线程TID,printf "%x" 转十六进制,jstack | grep -A 10 "nid=0x..."查RUNNABLE状态下的业务代码行。

Java 应用在 Linux 上 CPU 使用率突然飙到 100%,通常不是整个 JVM 占满,而是某个或某几个 Java 线程持续执行(比如死循环、频繁 GC、锁竞争、正则回溯等),导致对应内核线程占用大量 CPU。关键在于把高 CPU 的 Linux 线程(LWP)映射回 Java 线程,再找到其堆栈和具体代码行。
查哪个 Java 进程和线程最耗 CPU
先定位进程 ID(PID),再找它下面最“烫手”的线程:
- 用 top -H -p
:进入线程视图,按 P(大写)按 CPU% 降序,一眼看出 top 几个 LWP(线程 ID,即 TID) - 或用 ps -mp
-o tid,pcpu,time,comm :列出所有线程的 TID、CPU%、运行时间、命令名,同样按 CPU% 排序更直观 - TID 是十进制,后续要转成十六进制用于 jstack —— 记下那个 CPU% 最高的 TID(比如 12345)
把线程 ID 映射到 Java 线程名和堆栈
Java 的 jstack 输出中,线程的 native ID(nid)是十六进制,需将上一步的 TID 转为 hex:
- 执行 printf "%x\n" 12345 → 得到 3039(示例)
- 执行 jstack
| grep -A 10 "nid=0x3039" ,就能精准定位该线程的完整堆栈 - 注意:jstack 输出里每个线程块以 "java.lang.Thread.State: " 开头,后面跟着线程状态(RUNNABLE / BLOCKED / WAITING)和调用栈,重点关注 RUNNABLE 状态且处于业务代码中的帧
确认是否真正在执行业务逻辑(排除 GC/VM 线程干扰)
不是所有高 CPU 线程都代表你的代码有问题:
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
立即学习“Java免费学习笔记(深入)”;
- 检查堆栈顶部是否为 java.util.concurrent、sun.nio、org.apache.tomcat 等常见框架类 —— 这大概率是你自己的逻辑(比如 while(true) + sleep(0)、正则匹配超长字符串、HashMap 并发扩容死循环)
- 若看到 VM Thread、GCTaskThread、Finalizer 等,说明是 JVM 内部活动,需结合 jstat -gc
看是否频繁 GC;若看到 Attach Listener 或 Signal Dispatcher,基本可忽略 - 如果堆栈很深但全是 Unsafe.park 或 Object.wait,线程实际在等待,CPU 高可能是其他线程导致 —— 此时要多看几个高 CPU 线程,交叉分析
快速验证和收尾建议
定位到可疑方法后,别急着改代码,先做两件事:
- 用 kill -3
触发线程 dump(输出到 stdout 或 catalina.out),比 jstack 更轻量,适合生产环境高频采样(比如每 5 秒一次,连续抓 3 次对比) - 对疑似热点方法加日志或用 async-profiler(推荐):执行 ./profiler.sh -e cpu -d 30 -f profile.html
,生成火焰图,直观看到哪一行真正吃 CPU - 常见陷阱:String.replaceAll() 用错正则、SimpleDateFormat 非线程安全导致死锁式重试、CompletableFuture 无限递归、未关闭的 InputStream 导致 native 层忙等 —— 堆栈里出现这些类名要重点盯
不复杂但容易忽略:TID 是 Linux 线程 ID,不是 Java 线程 ID;jstack 的 nid 是十六进制;RUNNABLE 不等于“正在干活”,也可能是自旋等待。抓三次快照比单次更可靠。

















