CPU高占用排查和解决

使用 Arthas 排查 CPU 暴涨问题,比传统的 top + jstack 组合拳要高效和直观得多。它可以直接在交互界面中定位到具体的线程和代码行。

结合你之前运行的 CpuHog 程序,我们可以直接上手演练一遍。以下是使用 Arthas 排查 CPU 问题的标准流程:

🚀 第一步:启动 Arthas 并挂载目标进程

  1. 在服务器终端下载并启动 Arthas:
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
  1. 启动后,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 时间越多,一眼就能看出性能瓶颈在哪个方法。


💡 排查完成后:
输入 stopexit 退出 Arthas 即可。

在 Docker 环境下解决 CPU 过高的问题,核心思路分为两步:“快速定位”“施加限制”。因为容器共享宿主机的 CPU 资源,如果不加限制,单个容器的死循环或高负载很容易把整台服务器的 CPU 打满。

以下是具体的排查与解决步骤:

🔍 第一步:快速定位“肇事”容器

首先,你需要找出是哪个 Docker 容器占用了过高的 CPU。

  1. 实时监控所有容器
    在宿主机执行 docker stats 命令,它会实时刷新所有运行中容器的资源使用情况。观察 CPU % 这一列,找到持续占用率异常高的容器。
  2. 查看特定容器
    如果你已经怀疑某个容器,可以直接指定名称查看:docker stats <容器名称或ID>

🛠️ 第二步:进入容器内部定位问题代码

找到“肇事”容器后,排查逻辑和之前在普通服务器上排查 Java 进程完全一致,只是多了一步“进入容器”的操作:

  1. 进入容器内部
    执行 docker exec -it <容器名称或ID> /bin/bash(如果容器内没有 bash,可以尝试 /bin/sh)。
  2. 复用之前的排查方法
    进入容器后,你依然可以使用 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 限制,做好资源隔离,避免“一颗老鼠屎坏了一锅粥”。

posted @ 2026-05-06 20:04  圣祖帝皇  阅读(25)  评论(0)    收藏  举报