上周运维反馈线上程序出现了OOM,程序日志中的输出为java
Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMemoryError: Java heap space
Exception in thread "http-nio-8080-exec-1031" java.lang.OutOfMemoryError: Java heap space
复制代码
看线程名称应该是tomcat的nio工做线程,线程在处理程序的时候由于没法在堆中分配更多内存出现了OOM,幸亏JVM启动参数配置了-XX:+HeapDumpOnOutOfMemoryError,使用MAT打开拿到的hprof文件进行分析。spring
第一步就是打开Histogram看看占用内存最大的是什么对象: 数组
max-http-header-size: 10000000
复制代码
至此,基本已经肯定了八九不离十就是这个不合理的最大http请求头参数致使的问题。 到这里还有3个疑问:tomcat
先来看问题1,这个能够经过MAT在dump中继续寻找答案。 能够打开线程视图,搜索一下tomcat的工做线程,发现线程数量的确不少有401个,可是也只是800的一半: springboot
再来看看问题2,这就须要来找一下源码了,首先max-http-header-size是springboot定义的参数,查看springboot代码能够看到这个参数对于tomcat设置的是MaxHttpHeaderSize:bash
<attribute name="socket.appReadBufSize" required="false">
<p>(int)Each connection that is opened up in Tomcat get associated with
a read ByteBuffer. This attribute controls the size of this buffer. By
default this read buffer is sized at <code>8192</code> bytes. For lower
concurrency, you can increase this to buffer more data. For an extreme
amount of keep alive connections, decrease this number or increase your
heap size.</p>
</attribute>
复制代码
这也就是为何以前看到大量的buffer是10008192字节的。显然还有一批内容是空的10000000字节的buffer应该是output buffer,来看看源码: 服务器
至于问题3,显然咱们的应用程序是配置过最大线程的(查看配置后发现的确,咱们配置为了2000,好吧有点大),不然也不会有401个工做线程(默认150),若是当时并发并不大的话就一种可能,请求很慢,虽然并发不大,可是由于请求执行的慢就须要更多线程,好比TPS是100,可是平均RT是4s的话,就是400线程了。这个问题的答案仍是能够经过MAT去找,随便看几个线程能够发现不少线程都在等待一个外部服务的返回,这说明外部服务比较慢,去搜索当时的程序日志能够发现有不少"feign.RetryableException: Read timed out executing的日志"。。。。追杀下游去!慢点,咱们的feign的timeout也须要再去设置一下,别被外部服务拖死了。多线程