← 每日面试题 学习看板

CPU95线程800的GC连锁反应排查

面试题/未分类处理/2026/09/CPU95线程800的GC连锁反应排查.md

CPU95线程800的GC连锁反应排查

首次归档:2026-09-14 R1-round2 | 难度:进阶

来源文档:interview-2026-09-14-round2.md


原题

场景:

某 Java 服务 4 核 8G,上线 2 周后 CPU 从 30% 飙到 95%,线程数从 50 涨到 800,响应时间从 50ms 涨到 2s。监控显示:

问题:

  1. 分析 CPU 飙高的根因,给出定位步骤(工具链 + 关键指标)。
  2. 线程数从 50 涨到 800 的原因是什么?如何修复?
  3. 如何建立 CPU 飙高的预警机制,避免下次再发生?

我的回答(2026-09-14)

分析 GPU 飙高,首先看具体是哪个线程,可以通过 top 去查看具体的线程 ID,明确了 ID 之后可以看出来具体是哪个 ID,然后哪个 ID 导致信息又是过高。然后进一步想看内部细节的话,可以使用 Arris,然后登录它的看板,线程,Thread 去看一下具体是哪些线程导致的当前 CPU 过高占用,然后去明确线程的情况。 线程数上涨,从它的具体情况看,应该是大量的线程被占用了。然后把任务把线程栈打满,然后去扩展到非核心线程数,然后去进行使用,最终导致大量的线程创建。其实这个问题最终还是要确认为什么会有大量的线程超时,响应超时。这个超时的原因具体是因为什么?从当前得到的数据看,超时应该是因为 GC,大量的 GC,FullGC,5 分钟一次,其中堆内存有说 old 占的比例非常高的区,然后伊甸区频繁回收,也就是大量的 YoungGC 产生,这有可能是导致线程数暴涨的一个原因。

点评(2026-09-14)

优点:

不足:

得分:80/100

讲解

问题1:CPU 飙高定位步骤

因果链条:

代码问题(内存泄漏 + 大对象 + 锁竞争)
    ↓
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 分析

问题2:线程暴涨修复

// 修复线程池(队列缩小 + 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<>();

问题3:预警机制

指标警告阈值严重阈值
CPU 使用率> 80%> 90%
线程数> 200> 500
BLOCKED 线程> 50> 100
Full GC 频率> 1 次/小时> 3 次/小时
STW 耗时> 500ms> 1s
堆内存使用率> 80%> 90%

CI/CD 防回归:压测 30 分钟 + 阈值检查 + 阻断上线