一次非常典型的 JVM OOM 事故 (要注意 where 1 = 1 哦)

当面对 JVM OOM 时,你会紧张吗 ? 会不会手足无措 ?

这篇文章,分享前段时间帮一位同学梳理面对 JVM OOM 事故时的解题思路。

首先从对话中,我们可以看到内存溢出呈现两种情况:

  • 运行一段时间之后,CPU 飙高 ;
  • 服务假死,表现出来日志没有任何输出。

我的第一反应是:非常明显的 JVM 内存溢出表现 ,不过不知道是爆炸性的内存增长,还是缓慢的内存增长。

于是,我回复:可以每隔一段时间 观察 top -p Pid (进程号) 看看应用的内存占用情况。

类似的效果见下图:

接下来,我让他通过 jstat -gcutil pid 1000 看看 gc 的频率 。

从图中,新生代 E 区和老年代基本都满了 ,我基本可以确定是海量大对象产生导致 JVM OOM 了。

定时任务这四个字如电光火石般在我眼前闪过,基本八九不离十了。

接下来,他发了张那段时间的监控图:

哇,这张图太有画面感了,我都能感觉到 GC 线程在四处灭火,但依然无法释放内存的彷徨。

最后,我有点担心,是不是 JVM 内存分配小了才导致 OOM 了,同学的回复是 : 12 G 。

我觉得内存大小还可以 ,一般情况下通过 jmap -heap pid 来查看,示例图如下:

分析到这里,基本上我得到了如下的结论:

1、要查看代码中是否有一次性查询海量对象的操作 ;

2、或者有什么公共的对象一直在使用,而忘记了释放;

3、12 G 对一般的小应用来讲是绰绰有余的,而且他们的应用非高并发场景,是内网系统。

最后,我建议观察在日志停的那个时刻到底做了哪些事情,那才是真正的案发现场


那到底是什么原因导致 JVM OOM 呢 ? 和我预期的基本一模一样:

SQL 语句类似下图,查询条件没有拼接好,导致全表扫描。


我们总结下,解决 JVM 内存溢出问题的流程:

1、分析事故现场(CPU、内存、日志);

2、通过 top -p Pid (进程号)分析进程资源占用,判断是爆炸性的内存增长,还是缓慢的内存增长。

3、 jstat -gcutil pid 1000 看看 gc 的频率 ,可以分析是否有大对象产生以及 查看 GC 频率。

4、 jmap -heap pid 分析真实的 JVM 内存占用 ,确认是否真的内存分配得太小了。

5、 事故发生当时到底做了什么,有没有出现类似于内存或者 CPU 占用呈现脉冲飙高样子。

6、 若有飙高的场景,分析彼时彼刻到底有哪些操作。

7、 若是缓慢增长,则考虑使用 MAT 结合排除法分析内存占用。

上面的流程是我解决过内存溢出的套路,虽然很糙,但很实用,比如曾经帮助艺龙支付团队解决过订单查询内存溢出问题、西南某航空公司用户中心内存溢出问题等等。

最后,我想说:一定要注意 where 1 = 1 哦 ,真的出现太多次啦。

相关推荐
慕瑶琴43 分钟前
SQL语言的编译原理
开发语言·后端·golang
山海不说话2 小时前
从零搭建微服务项目Pro(第2-2章——JSR303自定义文件校验+整合至微服务公共模块)
java·spring boot·后端·spring·微服务
陆沙2 小时前
ASP.NET MVC-构建服务层+注入服务
后端·asp.net·mvc
luckyext4 小时前
Postman发送GET请求示例及注意事项
前端·后端·物联网·测试工具·小程序·c#·postman
架构文摘JGWZ4 小时前
SQLite?低调不是小众...
数据库·后端·学习·sqlite
uhakadotcom4 小时前
Pandas DataFrame 入门教程
后端·面试·github
Asthenia04124 小时前
深入理解 Java 中的 ThreadLocal:从传统局限到 TransmittableThreadLocal 的解决方案
后端
Asthenia04125 小时前
ThreadLocal原理分析
后端
绛洞花主敏明5 小时前
go中实现子模块调用main包中函数的方法
开发语言·后端·golang