CPU高占用排查和解决
使用 Arthas 排查 CPU 暴涨问题,比传统的 top + jstack 组合拳要高效和直观得多。它可以直接在交互界面中定位到具体的线程和代码行。
结合你之前运行的 CpuHog 程序,我们可以直接上手演练一遍。以下是使用 Arthas 排查 CPU 问题的标准流程:
🚀 第一步:启动 Arthas 并挂载目标进程
- 在服务器终端下载并启动 Arthas:
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
- 启动后,Arthas 会列出当前服务器上所有的 Java 进程。找到你的
CpuHog程序对应的序号,输入序号并回车,即可进入 Arthas 的交互控制台。
📊 第二步:使用 dashboard 查看整体概况(可选)
进入控制台后,输入 dashboard 命令并回车。
这个界面会实时展示 JVM 的整体状态。你可以观察最上方的线程列表,看看是否有线程的 %CPU 异常高;同时关注中间的内存(Memory)区域,确认 CPU 暴涨不是因为频繁的 Full GC(垃圾回收)导致的。按 q 键可以退出 dashboard。
🔥 第三步:一键定位高 CPU 线程(核心步骤)
这是最关键的一步。直接在控制台输入以下命令:
thread -n 3
这条命令的意思是:打印出当前最繁忙(CPU 占用最高)的前 3 个线程的堆栈信息。
执行后,你会看到类似下面的输出:
"cpu-hog-thread-0" Id=12 cpuUsage=99.8% RUNNABLE
at CpuHog.lambda$main$0(CpuHog.java:10) <-- 罪魁祸首就在这里!
at CpuHog$$Lambda$1/0x0000000800000a00.run(Unknown Source)
at java.lang.Thread.run(Thread.java:834)
cpuUsage=99.8%:明确告诉你这个线程占用了几乎 100% 的 CPU。at CpuHog.lambda$main$0(CpuHog.java:10):精准定位到了你的CpuHog.java第 10 行代码(也就是那个while(true)死循环)。
🔍 第四步:反编译验证线上代码(进阶)
如果你怀疑服务器上的代码和本地不一致(比如别人偷偷改了逻辑),你可以用 jad 命令直接反编译出问题的类,查看线上实际运行的代码:
jad CpuHog
这会把 CpuHog 类的源码直接打印在控制台上,方便你核对逻辑。
📈 第五步:生成火焰图(可视化分析)
如果 CPU 高的原因比较复杂(不是简单的死循环,而是多个方法调用耗时高),你可以生成火焰图来直观地看“热点”在哪里:
profiler start
# 等待 30 秒左右
profiler stop
执行 profiler stop 后,Arthas 会生成一个 .html 文件的路径。你可以把这个文件下载到本地,用浏览器打开。图表中越宽的“火焰”代表消耗的 CPU 时间越多,一眼就能看出性能瓶颈在哪个方法。
💡 排查完成后:
输入 stop 或 exit 退出 Arthas 即可。
在 Docker 环境下解决 CPU 过高的问题,核心思路分为两步:“快速定位” 和 “施加限制”。因为容器共享宿主机的 CPU 资源,如果不加限制,单个容器的死循环或高负载很容易把整台服务器的 CPU 打满。
以下是具体的排查与解决步骤:
🔍 第一步:快速定位“肇事”容器
首先,你需要找出是哪个 Docker 容器占用了过高的 CPU。
- 实时监控所有容器:
在宿主机执行docker stats命令,它会实时刷新所有运行中容器的资源使用情况。观察CPU %这一列,找到持续占用率异常高的容器。 - 查看特定容器:
如果你已经怀疑某个容器,可以直接指定名称查看:docker stats <容器名称或ID>。
🛠️ 第二步:进入容器内部定位问题代码
找到“肇事”容器后,排查逻辑和之前在普通服务器上排查 Java 进程完全一致,只是多了一步“进入容器”的操作:
- 进入容器内部:
执行docker exec -it <容器名称或ID> /bin/bash(如果容器内没有 bash,可以尝试/bin/sh)。 - 复用之前的排查方法:
进入容器后,你依然可以使用top命令查看容器内占用 CPU 最高的进程 PID。如果是 Java 应用,你依然可以把 Arthas 拷贝进容器,或者在宿主机通过jstack(需保证宿主机和容器内的 JDK 版本一致)来定位具体的代码行。
🛡️ 第三步:给容器加上 CPU“紧箍咒”(资源限制)
为了防止某个容器再次因为死循环或流量突增把宿主机 CPU 打满,必须在启动容器时加上 CPU 限制。这是 Docker 环境下解决 CPU 问题的治本之策。
你可以通过以下几种方式限制容器的 CPU 使用:
1. 限制 CPU 核心数(最常用)
使用 --cpus 参数,直接限制容器最多能使用多少个 CPU 核心。
- 例如,限制容器最多使用 1.5 个核心:
docker run -d --name my-app --cpus="1.5" my-image - 这样即使容器内出现死循环,它最多也只能吃满 1.5 个核,不会影响宿主机上的其他业务。
2. 绑定特定的 CPU 核心
使用 --cpuset-cpus 参数,将容器绑定到指定的 CPU 核心上运行,适合对性能要求极高的服务。
- 例如,只让容器使用 0 号和 1 号核心:
docker run -d --name my-app --cpuset-cpus="0,1" my-image
3. 动态更新限制(无需重启)
如果容器已经在运行,你不需要重启它,可以直接使用 docker update 命令动态调整限制:
docker update --cpus="1.0" <容器名称或ID>
4. 在 Docker Compose 中配置
如果你使用 docker-compose.yml 来编排服务,可以在配置文件中直接加入资源限制:
version: '3.8'
services:
app:
image: my-app-image
deploy:
resources:
limits:
cpus: '1.5' # 限制最多使用 1.5 核
总结建议:
当发现 Docker 环境 CPU 暴涨时,先用 docker stats 揪出元凶,然后进入容器用常规手段(如 Arthas)定位代码问题。同时,强烈建议为所有生产环境的容器配置 --cpus 限制,做好资源隔离,避免“一颗老鼠屎坏了一锅粥”。

浙公网安备 33010602011771号