事故概述

2025 年 11 月 11 日深夜,海外集群经历了两次服务中断,每次持续约 6-7 分钟。起初以为是网络抖动或业务高峰,但深入排查后发现,罪魁祸首是 GC 日志写入阻塞导致的 JVM 长时间停顿

问题排查

异常的 GC 日志

查看 GC 日志时发现:

[33979.477s][info][gc] GC(199) Pause Young (Normal) (G1 Evacuation Pause) 
1968M->133M(3072M) 111580.295ms

单次 Young GC 耗时 111 秒!

1968M->133M 的回收效果说明没有内存泄漏,日志也标注是普通的 Young GC。正常情况下,G1 的 Young GC 只需要几毫秒到几十毫秒。

翻了几个重启的 Pod 日志,都是类似问题:

account: GC pause 189 秒
config: GC pause 201 秒  
gateway: GC pause 176 秒

几个疑点:

  • 都是 Young GC,不是 Full GC
  • 跨不同节点都出现,排除单点硬件问题
  • 回收效果都很好,说明不是对象太多

当 GC 暂停时间异常长,但堆内存回收正常时,问题往往在外部。

寻找共性

仔细看时间点:

  • 第一波:23:44 - 23:50
  • 第二波:00:05 - 00:12

两次都是 5-7 分钟的窗口期,非常规律。这种规律性通常意味着外部因素。

多个服务、跨多个节点、同一时间窗口都 GC 卡顿,必然是某个共享的基础设施出问题了。

能让多个 Pod 同时受影响的共享资源:

  • 网络?但其他服务正常
  • 数据库?GC 不会访问数据库
  • 存储?如果多个 Pod 共享一个 NAS…

找到突破口

翻 deployment 配置,发现所有服务的 JVM 配置都有:

-Xlog:gc*=info:file=/xxx-pvc/gc_%p.log:...

/xxx-pvc 是个 PVC,挂的是 NAS,所有服务共享同一个 NAS 盘

如果 NAS 卡住了,会不会阻塞 GC?翻到了这篇文章印证了猜测: eBPF 求证坊间传闻:Java GC 日志可导致整个 JVM 服务卡顿?

验证

查看 NAS 监控:

NAS IOPS 监控

两个时间点 IOPS 监控直接归零:

  • 23:35-23:50
  • 00:05-00:15

和故障时间完全吻合。

确定了:NAS 挂了 → GC 日志写不进去 → JVM 在 STW 里等待 → 应用卡死

快速验证

临时把几个 Pod 的 GC 日志改到本地:

-Xlog:gc*=info:file=/tmp/gc_%p.log:...

重启后观察了一天,没再出现长时间 GC。

根本原因

问题链路

NAS 存储故障(IOPS=0) 
    ↓
GC 日志写入阻塞
    ↓  
JVM 在 STW 阶段等待日志写入
    ↓
应用长时间无响应(3+ 分钟)
    ↓
健康检查失败 → Pod 重启 → 级联重启

为什么 GC 日志会阻塞 STW

OpenJDK 的实现里,GC 日志是同步写入的:

// hotspot/share/gc/shared/gcTraceTime.cpp
GCTraceTime::~GCTraceTime() {
  LogStream ls(_out);
  ls.print(...);  // 同步写入
  ls.flush();     // 强制刷盘
}

GC 结束前,日志必须写完。底层文件系统卡住,整个 STW 就得等着。

这个设计是为了保证日志的顺序性和完整性,但副作用是日志 IO 成了 GC 的一部分

级联效应

单个服务 GC 卡住 3 分钟,为什么导致集群雪崩?

网关同步调用账户服务,QPS 200+。账户服务一卡,网关请求堆积:

网关线程池打满 → 健康检查超时 → K8s 重启 Pod

网关重启,依赖它的服务也开始健康检查失败,级联重启。

解决方案

立即修复

将 GC 日志改到本地磁盘:

# 修改前
-Xlog:gc*=info:file=/xxx-pvc/gc_%p.log:...

# 修改后  
-Xlog:gc*=info:file=/tmp/gc_%p.log:...

长期优化

GC 日志配置

  • 优先使用本地 SSD
  • 避免网络存储(NFS、NAS)

架构改进

  • 关键路径异步化,避免同步阻塞调用
  • 增加熔断降级和缓存兜底

监控完善

  • GC 暂停时间告警(建议 P99 > 1s)
  • 存储层 IOPS 监控

总结

一个看似无害的 GC 日志配置,在特定条件下也可能压垮系统。性能优化不仅要关注应用层,也要关注 JVM 和基础设施层

如果你的系统也在用网络存储记录 GC 日志,建议检查一下配置。