记一次生产事故:SkyWalking 导致 Metaspace OOM 问题复盘

最近线上遇到了一次 Metaspace 内存溢出导致的故障,最终定位到是 SkyWalking 引发的。排查过程虽然曲折,但也积累了不少经验,分享出来供大家参考。

事故经过

某天上班高峰期,线上服务突然不可用,接口全部报错。登录服务器查看日志,发现了大量 java.lang.OutOfMemoryError: Metaspace 错误。

服务重启后,没过多久又再次 OOM。那时候的心情,真的是一言难尽…

排查过程

第一反应:保留现场,快速恢复

发现 OOM 后,第一时间没有直接重启,而是采用了双节点策略:

  • 一个节点保留现场,用于排查问题
  • 另一个节点先重启恢复业务

这个策略是正确的,避免了业务长时间不可用。

艰难的根因定位

在保留的节点上开始排查。一开始看内存 dump 和业务日志,都没有发现明显异常。日志本身没有给出直接指向根因的线索。

后来在 OOM 堆栈中发现了 sw$ 前缀的类名,这才把怀疑方向指向了 SkyWalking。

查了一下版本,研究了 SkyWalking 的机制,才发现问题所在。

根因分析

SkyWalking 的工作机制

SkyWalking 通过 byte-buddy 对字节码进行增强,这是它正常的工作机制。它会为拦截到的类动态加载大量增强类,而且这些类设计上不会被卸载

随着服务运行,请求路径不断触发新的代码分支,SkyWalking 持续加载更多增强类,Metaspace 用量随运行时间渐进累积:

服务启动 → SkyWalking 开始拦截字节码,按需加载增强类
    ↓
每触达一条新代码路径 → 新的增强类被加载进 Metaspace
    ↓
这些类按机制设计不会被卸载
    ↓
运行一段时间后,Metaspace 逼近上限
    ↓
JVM 触发 Full GC 尝试回收 → 类无法卸载 → GC 白跑
    ↓
Metaspace 耗尽 → OOM

本质是容量规划问题

-XX:MaxMetaspaceSize=256M 从一开始就没有为 SkyWalking 的类加载量预留足够空间。时间越长矛盾越大,OOM 是必然结果,不是偶然

总结:SkyWalking 正常运行 + 容量规划不足 = 运行一段时间后必然 OOM

解决方案

立即修复

  1. 移除 SkyWalking Agent(如果暂时不需要链路追踪)
  2. 调整 Metaspace 大小:MaxMetaspaceSize=512M 或更大

监控告警必须加

这次故障本可以在业务无感知的情况下提前发现。需要接入以下告警:

告警项 说明
Metaspace 使用率 > 80% 约 8-9 小时时触发预警,可在业务低谷期处理
Full GC 频率 > 10次/小时 GC 死循环期间提前触发,比 OOM 早数小时
接口成功率骤降 服务异常时第一时间告警

JVM 关键指标(Metaspace、GC、类加载数)必须接入监控平台。

经验总结

做得好的地方

  1. 双节点策略:保留现场 + 快速恢复业务,两手准备
  2. 排查路径正确:从日志 → 堆栈 → 研究机制,最终定位根因

需要改进的地方

  1. 服务恢复后没有第一时间通知相关方:技术团队在埋头分析,但领导和业务方不知道服务已经恢复,信息断层持续了很长时间

排查建议

如果以后遇到类似的 Metaspace OOM 问题,可以按这个顺序排查:

# 1. 确认 OOM 类型
tail -n 200 application.log | grep "OutOfMemoryError"

# 2. 检查是否有 javaagent
ps -ef | grep javaagent

# 3. 查看 Metaspace 使用情况
jstat -gc <pid>

# 4. 如果有 javaagent,优先怀疑 Agent 类加载泄漏

运维建议

  1. 引入 JVM Agent 类组件时,一定要向开发团队同步其对 JVM 内存的影响
  2. 生产环境配置基线( JVM 参数、已安装的 Agent 版本)应该是团队共享文档
  3. 值班人员需要具备 jmapjstat、日志读取权限

附:Metaspace OOM 应急处理 SOP

【触发条件】接口告警 / 用户反馈服务不可用 / OOM 错误日志

1. 确认 OOM 类型
   tail -n 200 /path/to/application.log | grep "OutOfMemoryError"

2. 快速定位根因
   → 检查是否有 -javaagent(SkyWalking/Arthas 等)
   → 检查 MaxMetaspaceSize 配置

3. 保留现场 + 修复重启
   jmap -histo:live <pid> > /tmp/heap_histo.txt
   修改 JVM 参数(移除问题 javaagent 或调大 MaxMetaspaceSize)
   重启服务

4. 重启后立即对外通报
   "服务已恢复。根因:xxx。已采取措施:xxx。是否有残留风险:xxx。"

5. 验证稳定性
   jstat -gc <pid> 30000 5
   观察 Metaspace Used 是否平稳

技术问题的背后往往是管理问题。完善的知识共享机制、权限准备、监控体系、沟通流程,比单纯提升排查技术更能有效减少故障影响。

希望这次复盘对大家有帮助,也欢迎交流讨论。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐