GC日志中Times字段的user、sys、real分别表示用户态CPU时间、内核态服务耗时和STW墙钟时间;real明显大于user+sys(差值>5ms)且反复出现于Young GC,表明存在CPU核心抢占或调度延迟等底层资源瓶颈。

GC 日志里的 Times: user=0.12 sys=0.04, real=0.08 secs 不只是三个数字,它是 JVM 停顿期间 CPU 资源调度的“现场快照”。当 real 明显大于 user + sys(比如 real=12ms,user+sys=3ms),且反复出现在 Young GC 中,大概率不是 GC 算法问题,而是物理 CPU 核心被抢占——你的 GC 线程在排队等核用。
看懂 user/sys/real 的真实含义
别只盯着 real(STW 时长):
- user:JVM GC 线程在用户态执行代码所占用的 CPU 时间(如对象扫描、复制)
- sys:GC 过程中陷入内核态的时间(如分配内存页、获取锁、处理 TLB miss)
- real:从 GC 开始到结束的墙钟时间,包含所有等待——包括 CPU 调度延迟、核心争抢、上下文切换
关键点:real ≠ user + sys。差值(real − user − sys)就是“不可计算的等待时间”,其中 CPU 核心抢占是主因之一。
识别 CPU 核心抢占的典型日志信号
以下模式高度提示物理核心资源紧张:
- 同一台机器上多个 Java 进程的 GC 日志中,real 普遍比 user+sys 高出 3–10ms 以上,且波动剧烈
- Young GC 的 real 时间显著拉长,但 user 和 sys 却很稳定(说明计算和内核开销没变,只是“轮不到执行”)
- real > user + sys 的差值与
vmstat 1中的 r 列(可运行进程数)峰值强相关,例如 r ≥ CPU 核数 × 1.5 - G1 或 Parallel GC 日志中出现 “GC Workers: N”,但 real 接近单线程耗时 × N(说明线程没真正并行,都在排队)
联动系统指标确认核心抢占
仅靠 GC 日志不能下结论,必须交叉验证:
- 用
mpstat -P ALL 1观察各物理核的 %iowait 低但 %steal 或 %irq 异常高 → 宿主机超卖或中断风暴 - 查
cat /proc/<pid>/stat</pid>中的 utime/stime(累计用户/内核时间)增长缓慢,但 cutime/cstime(子进程时间)突增 → 其他进程吃满 CPU - 运行
perf record -e sched:sched_switch -p <pid> -g -- sleep 10</pid>,再perf script | grep -A5 "java"→ 若频繁看到 R state → S state 切换,说明线程长期处于可运行但未被调度状态
收敛环境、降低抢占影响
这不是 JVM 参数能调出来的,重点在运行时资源隔离:
- 容器部署时,禁用 CPU 共享模式(如 Kubernetes 的 cpu.shares),改用 cpu.cfs_quota_us + cpu.cfs_period_us 硬限频,避免被动态抢占
- 给 Java 进程绑定独占 CPU 核:
taskset -c 2-5 java -jar app.jar,并关闭对应核上的 irqbalance 和非必要服务 - 检查 BIOS 中是否启用 Intel Turbo Boost 或 AMD Precision Boost —— 频率动态升降会加剧调度抖动,生产环境建议锁定基础频率
- 避免在同一物理机混部高吞吐批处理任务(如 Spark、Flink)与低延迟 Java 服务

















