StringBoot下OOM问题排查

StringBoot下OOM问题排查

这个服务是一个相当于文件服务器,当服务收到文件后,将文件转存到fast-dfs上得到唯一id后处理业务。

0. 发现OOM情况

当文件服务出现自动重启的时候(服务是部署在Dokcer容器中的,服务的主线程挂掉,容器会自动重启)(容器的启动时间与其他服务时间不一致,因同一部署的时候,容器开始运行时间应该一致),认为可能是OOM问题,所以添加了执行参数上添加了-XX:+HeapDumpOnOutOfMemoryError(后知后觉),不出所料,在过了一段时间后,有出现服务重启,并产生了dump文件

1. 下载dump文件(拓展)

虽说是属于拓展,但是有特别重要,遇到问题的时候不一定能想起。
我们获取生产文件的时候(中小公司,大公司不一定是开发干的或者有专门运维工具处理的,也许吧),会发现,在内网传输数据的时候,在网络配置不是很好的情况下,直接下载很容易达到网络峰值,而且dump会很大(本例中大约4G)。
如果是从开发机获取生产机器的大文件的时候最好是限速下载,避免占满生产机器的网络。
这里推荐使用scp命令,linux、win、mac都自带这个命令的。
scp -l [limit] userName@serverIp:/root/xxx.dump ./xxx.dump
-l 后面的limit为要限制传输的带宽的数值,单位是Kbit/s

2. 分析dump文件

开始拿到文件,我使用的是java8中自带的jvisualvm
jvisualvm软件可以直接连接到本地的VM中,监视其VM情况,线程堆栈等。
但是特别需要注意这里上面使用的-XX:+HeapDumpOnOutOfMemoryError产生的是堆 Dump 文件,所以在jvisualvm使用装入,选择堆Dump文件,而不是添加VM核心dump文件。

image.png

在概要中,我们可以看到确实是发送OOM,是在http-nio-8776-exec-6线程中发生的,点击查看

image.png

发现程序在Http11InputBufferinit方法中申请内存导致OOM了。
为了查看“案发现场”,点击“类”

image.png

如图所示,byte[]占用了 98.8% 的空间,双击byte[]行,查看

image.png

在实例视图中我们看到,前500个实例中有部分大小特别大的实例。

image.png

点击byte[]实例,在字段视图中可以看到,只有前255有值,其余 204865535 byte 都为0值。

image.png

右击,显示最近的垃圾回收根节点。

image.png

确实也是由Http11InputBuffer类产生了大byte[]实例。

image.png

继续往下找,发现了个循环,双击查看。

image.png

发现了一个神奇的值,204857600,这个与byte[]实例数组大小部分相同,部分相似。

image.png

3. 继续分析

翻找源代码<tomcat-embed-core-8.5.34-sources.jar>发现绝对证据问题。

image.png

再向上查找,发现headerBufferSize值的来源是AbstractHttp11Protocol类中的maxHttpHeaderSize默认值为8*1024

image.png

找到maxHttpHeaderSize就想起了,在application.yml中有设置过它的大小。

image.png

当初也是在网络上看到设置maxHttpHeaderSize可以改变允许文件上传的大小。

server:
  max-http-header-size: 11  # 每次请求都会new 一个当前大小的baye内存。(适当设置)

4. 总结

  1. 实际操作中,上面的很多步骤都可以凭借经验省略掉,而且这个也不是唯一能够找到问题的路径,仅是本人个人学习的经验,应该会有错误。
  2. 找到问题出现的地方,后续就可以找解决方案,本文章中出现的问题,初步估计可以降低maxHttpHeaderSize大小,调整服务的内存和并发量,来解决问题。(欢迎提供思路和解决方案)

后记
如果想看示例中byte[]是什么内容,jvisualvm本人暂时没有找到查看方式,可以使用 Eclipse MAT 查看,如下图。(看到内容也确认了是请求headerBuffer的大小设置的有问题)

image.png