top看到java进程%CPU 780%,%MEM加起来超过100%——这些数字到底什么意思?
本文目录
先说结论
%CPU 是按单核算的,多线程进程可以轻松超过 100%。一个 8 核机器上,java 进程用满所有核就是 800%。top 顶部 `Cpu(s)` 那一行才是整机平均,那个不会超过 100%。
%MEM 是 RSS/总内存,多进程加起来可以超过 100%,因为 RSS 里包含了共享内存(比如 libc、jar 包 mmap 的部分),多个进程会重复计算同一块物理内存。看真实占用要看 PSS(/proc/
ps 的 %CPU 和 top 的 %CPU 算法不一样。ps 算的是进程从启动到现在的平均 CPU(累积 CPU 秒 / 进程运行时长),top 算的是最近一个刷新间隔内的 CPU。一个刚起的进程跑满 CPU,top 显示 100%,ps 可能只显示 20%。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
这道题看起来是送分题,实际挺多人答错。top 看到一个 java 进程 %CPU 780%、几个进程 %MEM 加起来 180%——这到底合不合理?为什么会超过 100%?%CPU 和 ps 输出的 CPU 为什么对不上?容器里看到的 %CPU 还能信吗?能把这几个细节讲清楚的,一眼就能看出是有经验的老手。
业务场景:Spark executor %CPU 780% 被告警系统误判
我在某出海硬件品牌做 Databricks 的时候,运维搭了一套自研的监控告警,规则里有一条是 '单进程 %CPU > 300% 就告警'。某天早上一看,几十个 Spark executor 全触发了 780%、650% 的告警,运维群炸了。新来的运维同学以为是集群失控,准备 kill 作业回滚。我拦下来看了一眼 top,告诉他:
# 机器配置:32 核 128G
lscpu | grep -E 'CPU\(s\)|Thread'
# CPU(s): 32
# Thread(s) per core: 2
# top 看 java 进程
top -bc -n 1 | head -20
# PID USER %CPU %MEM COMMAND
# 12345 spark 780 35 java -Xmx30g ...Executor
# 23456 spark 650 32 java -Xmx30g ...Executor
# top 顶部:
# %Cpu(s): 85.2 us, 3.1 sy, 0.0 ni, 11.5 id <-- 整机平均
# 说明:
# - 单个进程 %CPU 780% = 用了 7.8 个核
# - 整机 32 核 × 85.2% ≈ 27.3 个核在忙,合理
# - 多个 executor 是正常并行吃满核心
这个告警规则完全是写告警的人不懂 Linux 多核。正确的告警应该是 `%CPU > 核数 × 80`,而不是一个写死的百分比。后来我帮他们把告警逻辑改对了,顺便把类似的几条 '历史遗留' 告警全梳理了一遍。
手段一:top 顶部几行数据的正确解读
top 输出分三块:顶部的整机汇总、中间的任务和内存汇总、下面的进程列表。很多人只看进程列表,顶部那几行其实信息量最大。
top
# 第 1 行:load average
# top - 14:23:15 up 127 days, load average: 15.2, 14.8, 12.3
# load 的 1/5/15 分钟平均值。32 核机器 load 15 只占一半,不算忙。
# 第 2 行:任务数
# Tasks: 421 total, 3 running, 418 sleeping, 0 stopped, 0 zombie
# running 是真在跑的,sleeping 多数是等 IO 或等事件
# 第 3 行:CPU 整机平均(关键!)
# %Cpu(s): 85.2 us, 3.1 sy, 0.0 ni, 11.5 id, 0.1 wa, 0.0 hi, 0.1 si, 0.0 st
# us = 用户态(业务代码)
# sy = 内核态(系统调用、IO)
# ni = nice 调整过的进程
# id = 空闲
# wa = 等 IO
# hi = 硬中断
# si = 软中断
# st = steal(虚拟化被宿主机抢走的时间)
# 重点:按 '1' 展开看每个核
# 如果 us 高:业务代码热
# 如果 sy 高:系统调用多(频繁读写、大量上下文切换)
# 如果 wa 高:IO 瓶颈
# 如果 st 高:虚拟化资源被抢
# 第 4 行:内存汇总
# KiB Mem: total, free, used, buff/cache
# KiB Swap: total, free, used
最容易忽略的指标:`wa`(iowait)和 `st`(steal)。wa 高说明 CPU 在等 IO,看起来 CPU 忙其实在发呆;st 高说明你在虚拟机里,宿主机别人抢走了你的 CPU 时间片。云主机上 st 超过 5% 就要警惕,可能是同宿主机有别的租户在吃 CPU。
手段二:%CPU 超过 100% 的原理
很多人理解不了这一点。top 的 %CPU 本质是 '过去一个刷新周期内,这个进程用了多少 CPU 时间'。单位是'核·秒' / '墙上秒'。
# 原理:
# 过去 1 秒内(刷新周期),进程的所有线程加起来
# 用了多少 CPU 时间 × 100 / 1 秒
# 例:
# 一个 java 进程有 10 个 worker 线程
# 过去 1 秒内,每个线程用了 0.78 秒的 CPU
# 总 CPU 时间 = 10 × 0.78 = 7.8 秒
# %CPU = 7.8 / 1 × 100 = 780%
# 验证:
top -H -p 12345 # 切换到线程视图
# 会看到 10 个线程,每个 78% 左右
# 加起来就是进程的 780%
# 换算公式:
# 一个进程真正'占了多少个核' = %CPU / 100
# 780% = 7.8 个核
# 整机 %CPU 上限 = CPU 核数 × 100
# 32 核机器理论上限 = 3200%
# 看机器核数
nproc
cat /proc/cpuinfo | grep processor | wc -l
lscpu
手段三:%MEM 超过 100% 的原因(共享内存)
%MEM 超过 100% 这个现象更隐晦,但原理类似。ps/top 的 %MEM 算的是 `RSS / 总物理内存`,而 RSS 里把 '这个进程映射到的所有物理页' 都算进去,包括那些和别的进程共享的页。
# 看进程内存占用的几种指标
ps -o pid,rss,vsz,pmem,cmd -p 12345
# PID RSS VSZ %MEM CMD
# 12345 8456012 10245680 13.2 java ...
# RSS = Resident Set Size 常驻内存(包含共享)
# VSZ = Virtual Size 虚拟内存(更虚)
# %MEM = RSS / MemTotal
# 真实独占内存(PSS,Proportional Set Size)
# 共享页按'使用进程数'平分
cat /proc/12345/smaps_rollup
# 或者聚合 smaps
grep -e '^Pss' /proc/12345/smaps | awk '{sum+=$2} END {print sum " kB"}'
# 更细致:看 RSS 里有多少是共享的
cat /proc/12345/status | grep -E 'VmRSS|RssAnon|RssFile|RssShmem'
# VmRSS: 8256012 kB <-- 总 RSS
# RssAnon: 7580000 kB <-- 真正独占(堆、栈)
# RssFile: 650000 kB <-- mmap 的文件(共享的大头)
# RssShmem: 26012 kB <-- tmpfs/shmem
# 面试加分:说 RssAnon 才是真正的独占内存
典型案例:机器上跑了 20 个 JVM 进程,每个 %MEM 8%,加起来 160%。这不是内存穿越,是因为 20 个进程都 mmap 了同一批 jar 包和 libc.so,这些共享页被重复统计了。实际物理内存占用远小于 160%。
手段四:ps 和 top 的 %CPU 为什么对不上
经典问题:同一时刻 top 看某进程 %CPU 是 500%,ps -eo pid,pcpu 看同一个 pid 是 180%,怎么回事?
# top 的 %CPU:过去一个刷新周期(默认 3 秒)内的瞬时值
# ps 的 %CPU:进程从启动到现在的累积平均 = CPU_TIME / ELAPSED_TIME
# 验证
ps -p 12345 -o pid,pcpu,time,etime,cmd
# PID %CPU TIME ELAPSED CMD
# 12345 180 5:23 3:00 java ...
# 意思:进程启动 3 分钟,累积用了 5 分 23 秒 CPU
# 180% = 5:23 / 3:00 * 100
# 所以:
# - 刚启动的进程吃满 CPU,top 看 800%,ps 看很低,因为平均下来没多久
# - 跑了很久平均很高但最近没事的进程,ps 看 300%,top 看 5%
# 排查实时 CPU 异常必须用 top,不能用 ps
# 复盘历史 CPU 趋势可以用 ps(累积更稳定)
# 看进程累积了多少 CPU 秒
cat /proc/12345/stat | awk '{print "utime:"$14" stime:"$15}'
# utime = 用户态 CPU 时钟数
# stime = 内核态 CPU 时钟数
# 除以 sysconf(_SC_CLK_TCK)(一般是 100)得秒数
几个常用命令的 CPU/内存指标对比
| 命令 | %CPU 是什么 | %MEM 是什么 | 适用场景 |
|---|---|---|---|
| top | 最近刷新周期的瞬时 | RSS/MemTotal | 实时观察 |
| ps -o %cpu | 累积平均 | RSS/MemTotal | 历史均值 |
| pidstat -u 1 | 每秒瞬时 | - | 连续采样、细节 |
| /proc/pid/stat | 原始 tick 数 | - | 脚本精确计算 |
| /proc/pid/smaps | - | PSS(比 RSS 准) | 共享内存精确 |
面试高频追问
追问一:load average 15 对一台 32 核机器来说是忙还是闲?
不忙。load 的含义是 '过去 1/5/15 分钟处于 RUNNING 或 UNINTERRUPTIBLE 状态的进程数平均值'。粗略换算:load / 核数 ≈ CPU 负载率。32 核机器 load 15 ≈ 47% 负载,属于正常。经验阈值:load / 核数 < 0.7 闲、0.7-1 紧、>1 超载。但要注意 load 包含了 UNINTERRUPTIBLE(D 状态,一般是等磁盘 IO),如果 load 很高但 %Cpu 很闲,很可能是 IO 瓶颈不是 CPU 瓶颈,这是区分 CPU 瓶颈和 IO 瓶颈的关键信号。
追问二:top 里 %Cpu st 超过 20% 意味着什么?
st 是 '虚拟化被宿主机抢走的 CPU 时间'。如果你在云主机(AWS EC2、阿里云 ECS)上看到 st > 20%,说明你所在的宿主机上有其他租户在抢 CPU,或者你订阅的是 burstable 实例型号(比如 t 系列)CPU 积分用光被限流了。表现就是你的程序明明看起来 CPU 没满但就是慢。解决办法:① 升级到性能稳定型(c 系列);② 换宿主机(在云控制台重启或者 stop+start,有概率换到不忙的宿主机);③ 联系云厂商支持。我在某元宇宙社交应用的阿里云 ECS 上就遇到过连续几天 st 10-15%,工单反馈后阿里帮我们换了宿主机,st 直接降到 0。
追问三:怎么精确测一个进程用了多少 CPU 秒?
三个办法。① `ps -p
实战踩坑:告警规则写 '%CPU > 300% 告警'
某出海硬件品牌的自研监控平台,刚搭起来的时候写了一条告警:'单进程 %CPU > 300% 持续 1 分钟告警'。结果上线第二天运维群就被 Spark executor 的告警刷屏,因为一个 executor 正常跑就是 500-700% CPU(它有十几个 worker 线程并行)。
# 错误规则:
# if process.%cpu > 300: alert
# 正确规则(按核数归一化)
# if process.%cpu / num_cores * 100 > 80: alert
# Prometheus 告警示例
- alert: ProcessHighCPU
expr: |
(process_cpu_seconds_total:rate1m * 100)
/
(count(count by (cpu) (node_cpu_seconds_total)))
> 80
for: 2m
# 另一个类似错误:%MEM > 50% 告警
# 正确:按机器总内存 × 核数 归一化
# 一个 30G heap 的 java 进程在 256G 机器上 %MEM 只有 12%
# 在 32G 机器上 %MEM 会接近 95%
# 绝对阈值毫无意义
经验:所有和 %CPU、%MEM 相关的告警规则,都必须按机器规格归一化。否则同一个作业在不同规格机器上行为不同,你的告警系统就是随机响铃。我在每个新接手的监控平台都要首先检查这一条。
总结
%CPU 和 %MEM 的那些 '大于 100%' 不是 bug 是特性:%CPU 是单核基准所以多线程必然超,%MEM 是 RSS 包含共享内存所以多进程必然超。面试能说清楚 top 顶部 `%Cpu(s)` 的 us/sy/wa/st 各是什么、能解释 RSS/VSZ/PSS 的差异、能指出 ps 和 top 的 CPU 算法不同、能举出一个告警规则踩坑的例子——这一题基本就满分了。
下一篇聊一个更深的线上场景:一个 Flink TaskManager 卡死了但日志没报错——lsof / strace / /proc 三板斧怎么用来定位卡在哪?
你们有没有踩过写错监控阈值的坑?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长