首次归档:2026-09-14 R1-round2 | 难度:进阶
来源文档:interview-2026-09-14-round2.md
场景:
某 Java 服务 4 核 8G,上线 2 周后 CPU 从 30% 飙到 95%,线程数从 50 涨到 800,响应时间从 50ms 涨到 2s。监控显示:
synchronized 块问题:
分析 GPU 飙高,首先看具体是哪个线程,可以通过 top 去查看具体的线程 ID,明确了 ID 之后可以看出来具体是哪个 ID,然后哪个 ID 导致信息又是过高。然后进一步想看内部细节的话,可以使用 Arris,然后登录它的看板,线程,Thread 去看一下具体是哪些线程导致的当前 CPU 过高占用,然后去明确线程的情况。 线程数上涨,从它的具体情况看,应该是大量的线程被占用了。然后把任务把线程栈打满,然后去扩展到非核心线程数,然后去进行使用,最终导致大量的线程创建。其实这个问题最终还是要确认为什么会有大量的线程超时,响应超时。这个超时的原因具体是因为什么?从当前得到的数据看,超时应该是因为 GC,大量的 GC,FullGC,5 分钟一次,其中堆内存有说 old 占的比例非常高的区,然后伊甸区频繁回收,也就是大量的 YoungGC 产生,这有可能是导致线程数暴涨的一个原因。
优点:
不足:
得分:80/100
因果链条:
代码问题(内存泄漏 + 大对象 + 锁竞争)
↓
Young GC 频繁
↓
Old 区快速填满
↓
Full GC 频繁(STW 1.5s)
↓
线程执行变慢
↓
任务堆积 → 线程池创建新线程
↓
线程数 50 → 800
↓
CPU 飙高
工具链:
Step 1: top → 找高 CPU 进程
Step 2: top -Hp <pid> → 找高 CPU 线程
Step 3: printf '%x' <tid>
Step 4: Arthas thread -n 3
Step 5: Arthas profiler
Step 6: heapdump --live
Step 7: MAT 分析
// 修复线程池(队列缩小 + CallerRunsPolicy)
new ThreadPoolExecutor(50, 100, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy());
// 修复内存泄漏(用 Caffeine 替代静态 Map)
Cache<String, Object> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofMinutes(30))
.build();
// 修复锁竞争(缩小锁粒度、用并发集合、读写锁)
ConcurrentHashMap<String, Object> state = new ConcurrentHashMap<>();
| 指标 | 警告阈值 | 严重阈值 |
|---|---|---|
| CPU 使用率 | > 80% | > 90% |
| 线程数 | > 200 | > 500 |
| BLOCKED 线程 | > 50 | > 100 |
| Full GC 频率 | > 1 次/小时 | > 3 次/小时 |
| STW 耗时 | > 500ms | > 1s |
| 堆内存使用率 | > 80% | > 90% |
CI/CD 防回归:压测 30 分钟 + 阈值检查 + 阻断上线