这个服务是一个相当于文件服务器,当服务收到文件后,将文件转存到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文件。
在概要中,我们可以看到确实是发送OOM,是在http-nio-8776-exec-6
线程中发生的,点击查看
发现程序在Http11InputBuffer
类init
方法中申请内存导致OOM了。
为了查看“案发现场”,点击“类”
如图所示,byte[]
占用了 98.8% 的空间,双击byte[]
行,查看
在实例视图中我们看到,前500个实例中有部分大小特别大的实例。
点击byte[]
实例,在字段视图中可以看到,只有前255有值,其余 204865535 byte 都为0值。
右击,显示最近的垃圾回收根节点。
确实也是由Http11InputBuffer
类产生了大byte[]
实例。
继续往下找,发现了个循环,双击查看。
发现了一个神奇的值,204857600,这个与byte[]实例数组大小部分相同,部分相似。
3. 继续分析
翻找源代码<tomcat-embed-core-8.5.34-sources.jar>发现绝对证据问题。
再向上查找,发现headerBufferSize
值的来源是AbstractHttp11Protocol
类中的maxHttpHeaderSize
默认值为8*1024
找到maxHttpHeaderSize
就想起了,在application.yml
中有设置过它的大小。
当初也是在网络上看到设置maxHttpHeaderSize
可以改变允许文件上传的大小。
server:
max-http-header-size: 11 # 每次请求都会new 一个当前大小的baye内存。(适当设置)
4. 总结
- 实际操作中,上面的很多步骤都可以凭借经验省略掉,而且这个也不是唯一能够找到问题的路径,仅是本人个人学习的经验,应该会有错误。
- 找到问题出现的地方,后续就可以找解决方案,本文章中出现的问题,初步估计可以降低
maxHttpHeaderSize
大小,调整服务的内存和并发量,来解决问题。(欢迎提供思路和解决方案)
后记
如果想看示例中byte[]
是什么内容,jvisualvm本人暂时没有找到查看方式,可以使用 Eclipse MAT 查看,如下图。(看到内容也确认了是请求headerBuffer的大小设置的有问题)