当前位置: 首页 > news >正文

记一次线上OOM排查,JVM调优全过程

周三下午,正在摸鱼,突然钉钉群里炸了:

[告警] 订单服务 POD重启 [告警] 订单服务 POD重启 [告警] 订单服务 POD重启

3个Pod连续重启,打开监控一看,内存直接打满然后被K8s杀掉了。

经典的OOM。

现象

  • 服务:订单服务(Java,Spring Boot)
  • 部署:K8s,3个Pod,每个限制4G内存
  • 现象:内存缓慢增长,到达4G后被OOM Kill
  • 频率:每隔2-3小时重启一次

第一反应:加内存?

领导说:内存不够就加嘛。

我:…加内存治标不治本,得找到根因。

排查过程

1. 先看GC日志

# JVM参数里加上GC日志-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/logs/gc.log

等了一个小时,服务挂了,捞出GC日志:

[Full GC (Allocation Failure) 3800M->3750M(4096M), 5.234 secs] [Full GC (Allocation Failure) 3780M->3760M(4096M), 5.567 secs] [Full GC (Allocation Failure) 3790M->3785M(4096M), 6.012 secs]

Full GC后内存几乎没释放,说明有内存泄漏,有东西一直占着不放。

2. dump内存

在容器里加了个脚本,OOM前自动dump:

# JVM参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/heap.hprof

但K8s的OOM Kill太快了,还没来得及dump就被杀了。

换个方式,手动dump:

# 找到Java进程PIDjps# 手动dump(等内存涨到3G左右时执行)jmap -dump:format=b,file=/logs/heap.hprof<pid>

dump出来一个3G的文件。

3. 分析heap dump

把文件拷到本地,用MAT(Memory Analyzer Tool)打开。

Leak Suspects Report: Problem Suspect 1: 256,789 instances of "com.xxx.OrderDTO" 占用内存:2.1 GB (54%)

25万个OrderDTO对象?这订单量也没这么大啊。

点进去看引用链:

java.util.concurrent.ConcurrentHashMap -> com.xxx.cache.LocalCache -> OrderDTO (256789 instances)

LocalCache,本地缓存。

4. 定位代码

// LocalCache.javapublicclassLocalCache{privatestaticfinalMap<String,OrderDTO>cache=newConcurrentHashMap<>();publicstaticvoidput(StringorderId,OrderDTOorder){cache.put(orderId,order);}publicstaticOrderDTOget(StringorderId){returncache.get(orderId);}// 没有remove方法// 没有过期机制// 没有大小限制}

经典错误:只往缓存里放,不清理。

一查代码提交记录,是3个月前一个同事为了"优化性能"加的,从那之后这个服务就开始间歇性OOM。

5. 修复

方案一:直接删掉这个缓存(最简单)

方案二:换成带过期的缓存

// 用Caffeine替代privatestaticfinalCache<String,OrderDTO>cache=Caffeine.newBuilder().maximumSize(10000)// 最多1万条.expireAfterWrite(5,TimeUnit.MINUTES)// 5分钟过期.build();publicstaticvoidput(StringorderId,OrderDTOorder){cache.put(orderId,order);}publicstaticOrderDTOget(StringorderId){returncache.getIfPresent(orderId);}

上线后,内存稳定在1.5G左右,再也没OOM过。

JVM参数调优

趁这个机会,把JVM参数也优化了一下。

之前的配置

-Xms2g -Xmx4g# 就这两个参数...

优化后的配置

# 堆内存-Xms4g -Xmx4g# 初始和最大一样,避免动态调整-XX:NewRatio=2# 年轻代:老年代 = 1:2# GC选择(JDK11+推荐G1)-XX:+UseG1GC -XX:MaxGCPauseMillis=200# 目标停顿时间200ms-XX:G1HeapRegionSize=8m# Region大小# 元空间-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m# GC日志-Xlog:gc*:file=/logs/gc.log:time,level,tags:filecount=5,filesize=100m# OOM时dump-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/# 容器感知(JDK8u191+)-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0# 使用容器内存的75%

监控指标

加了几个关键监控:

# Prometheus指标-jvm_memory_used_bytes-jvm_gc_pause_seconds_sum-jvm_gc_pause_seconds_count-jvm_classes_loaded_classes_total

设置告警:

  • 老年代使用率 > 80% 持续5分钟
  • Full GC 频率 > 1次/分钟
  • GC停顿时间 > 1秒

排查工具汇总

场景工具命令
查看堆内存jmapjmap -heap
dump内存jmapjmap -dump:format=b,file=heap.hprof
分析dumpMAT图形界面
实时监控jstatjstat -gcutil 1000
查看线程jstackjstack
在线分析Arthasdashboard/heapdump

Arthas神器

推荐用Arthas,在线诊断特别方便:

# 下载启动curl-O https://arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar# 选择要attach的进程后:# 查看堆内存dashboard# 查看最占内存的对象heapdump --live /tmp/heap.hprof# 查看某个类的实例数sc -d com.xxx.OrderDTO vmtool --action getInstances --className com.xxx.OrderDTO --limit10# 查看方法调用watchcom.xxx.LocalCache put'{params, returnObj}'-x2

远程调试

这次OOM排查还好在公司,能直接连到服务器。

如果在家收到告警,K8s集群在公司内网,怎么办?

之前的方案是远程专线,但经常断,而且手机上操作kubectl很难受。

后来用星空组网把电脑和跳板机组到一起,在家也能kubectl进容器排查了。

总结

这次OOM排查的经验:

步骤工具/方法
1. 确认OOM监控告警、GC日志
2. dump内存jmap / HeapDumpOnOutOfMemoryError
3. 分析dumpMAT / jhat
4. 定位代码引用链分析
5. 修复上线代码修改
6. 加固监控JVM监控指标

常见的内存泄漏原因:

  • 缓存无限增长(本次)
  • 静态集合持续添加
  • 未关闭的连接/流
  • ThreadLocal使用不当
  • 监听器未注销

最后,写缓存的时候一定要想清楚:

  • 什么时候过期?
  • 最大存多少?
  • 怎么清理?

不然就等着半夜被电话叫醒吧。


有JVM相关问题欢迎评论区交流。

http://www.cnnetsun.cn/news/179432.html

相关文章:

  • 股票基础-第32课-投资组合构建与管理
  • 【电商运营必看】Open-AutoGLM如何实现98%好评回复满意度?
  • 【高可用架构设计】:基于Open-AutoGLM的电商库存自动监控系统搭建指南
  • 【电商运营效率提升300%】:Open-AutoGLM自动化报名落地全攻略
  • AI生成圣诞视觉图:从节日元素到创意落地的路径
  • 揭秘Open-AutoGLM自动报名系统:如何3步完成电商大促流量收割
  • Open-AutoGLM如何重构电商运营?:5大核心模块深度解析与落地指南
  • 零基础学网安,NISP 证书到底值不值?别白花钱还没效果!
  • PCB蚀刻常见缺陷-资深工程师的经验总结
  • COMSOL模拟:压电-热释电纳米发电系统中的压电薄膜三维模型文章复现
  • 鸿蒙前端开发,零基础入门到精通,收藏这篇就够了
  • vscode怎么启动前端项目,零基础入门到精通,收藏这篇就够了
  • 一文搞懂:AI Agent 八大核心概念(小白程序员收藏版)
  • 收藏!大龄程序员转型难在哪?4大核心痛点拆解+破局方向
  • 【Open-AutoGLM电商评价自动回复】:揭秘AI自动生成高转化率评价回复的底层逻辑
  • 9款AI写论文哪个好?实测对比后,只有宏智树AI能一键生成带真实数据图表+知网可查文献的毕业论文
  • 从泄露到合规:Open-AutoGLM日志权限改造全流程(含RBAC模型落地细节)
  • 阻塞队列:线程池核心机制take() vs poll()
  • 【2025最新】基于SpringBoot+Vue的宠物商城网站管理系统源码+MyBatis+MySQL
  • LangFlow Reactor反应器模式响应事件
  • ECharts 饼图(Pie Chart)教程
  • Open-AutoGLM日志加密部署难题:90%团队忽略的2个致命风险点
  • 精密机械工厂6个研发如何共享一台SolidWorks云工作站
  • Open-AutoGLM监控总失效?99%人忽略的3个配置陷阱
  • LangFlow静态站点生成(SSG)可行性探讨
  • Linux 如何设置开机自启:全面指南!
  • Docker Compose 实战教程,理解Docker Compose核心概念,学会编写 compose.yml,掌握常用命令!
  • 科研征途的“智慧导航”:书匠策AI文献综述功能开启学术新视界
  • C语言程序设计基础入门
  • 地埋式积水监测站:道路积水监测系统