【OOM】记一次线上OOM解决全流程

一、OOM背景

  疫情期间,大家都开始了远程办公。刚开始不适应,最后感觉还挺好的,不用每天挤地铁,住8平米的出租屋。

  忽然有一天,系统报警邮件来了,运维也在群里艾特我,系统OOM了。其实写Java的同学如果自己负责的系统出现了OOM,是很尴尬的事情。

  毕竟也是骨灰级玩家了,不慌不忙。考虑到远程机器的不便,我果断要求运维拿下dump.hprof文件给我。没想到运维说并没有输出,我此时也不想过问为何没输出了。可能运维把启动参数去掉了,也可能内存占用太大了输出不了。。。

  好吧!那就强制输出下吧!jmap -dump:live,format=b,file=/tmp/application.hprof pid   一条强制输出内存日志的命令扔给运维同学。顺利拿到了文件。拿到内存文件后(3个G的dump.hprof),运维暂时重启了本服务。

  其实这个项目以前运维找我说过,项目跑一段时间后,内存占用大概就在1.5G-2G之间(这个项目作为本公司并发最大的项目之一,我们给了4G内存),我本想抽个时间好好分析下内存占用的,后来事情比较多,反正项目还能继续用,也就暂时搁置了。(可能是创业公司的通病,一切以暂时能用为目标!)

 

二、解决思路

       拿到dump.hprof文件,我打开了神器--“MemoryAnalyzer”  (https://www.cnblogs.com/kbian/p/11780508.html),本次内存溢出的错误:java.lang.OutOfMemoryError: Java heap space。堆内存占用过高!我就看看哪些东西吃了了这么多内存,新账旧账一块算!直接打开直方图

 这个byte[]占用了1.5G,继续深究!看看是哪些对象。with all references

 

 

看不知道,一看吓一跳,好多个tomcat线程,每个占用了20M

 

 

点开其中一个线程继续跟踪!可以看到很明显这和HTTP请求相关。

tomcat的线程在处理过程中分配了10M的buffer在堆上,一个线程分配了输入输出两个buffer,占用20M内存。

马上可以想到可能是tomcat什么参数设置的不合理导致了这种情况,一个请求线程不可能占用20M。赶紧看看application.yml

 

果然,http的header分配了10M的内存!输入加输出,一个线程20M没错了!

 

三、验证

  在测试环境将 max-http-header-size直接去掉。(默认值只有 4096 个字节(4k)),push代码,发布完成!跑了几个小时,让运维同学看了下,该项目从常用内存2G变为了700M!拿到dump.hprof文件看了下,20M的tomcat请求已经不存在了。

       嗯!700M?现在春节放假,并发不多,还有优化空间嘛!下次再说了!

 

四、复盘

  为何max-http-header-size 设置为10M就会占用20M呢?其实我看了header里面的内容,不超过1K。

  查阅资料说,tomcat每次一个http线程,都会开启 input headerbuffer、output headerbuffer。一个headerbuffer分配的大小是:MaxHttpHeaderSizeReadBuffer + ReadBuffer。ReadBuffer默认是8K。

       这就对了!

 

五、小结

  1、知其然知其所以然!不通源码的程序员不是一个架构师!源码学习之路任重道远。。。

  2、写代码脑子里要装着JVM,OOM是程序员之耻。。。。。千万不要犯

 

posted @ 2020-02-24 13:03  得记点什么了  阅读(2040)  评论(1编辑  收藏  举报