上周运维反馈线上程序出现了 OOM,日志中频繁抛出异常:
Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMemoryError: Java heap space
看线程名称应该是 Tomcat 的 NIO 工作线程,在处理请求时因无法在堆中分配更多内存而崩溃。幸好 JVM 启动参数配置了 -XX:+HeapDumpOnOutOfMemoryError,拿到了 hprof 文件后用 MAT 进行分析。
拿到 dump 文件后,我习惯先打开 Histogram 看看占用内存最大的对象是什么。



从数据来看,基本可以确定是配置不合理的最大 HTTP 请求头参数导致的问题。配置项如下:
max-http-header-size: 10000000
这里有几个疑问需要厘清:即使一个请求分配 10M 内存,堆有 8GB,难道当时有这么多并发吗?800 个 Tomcat 线程?参数只是设置了最大请求头 10M,为什么 Tomcat 就会一次性分配这么大的 buffer 呢?为什么会有如此多的 Tomcat 线程?感觉程序没这么多并发。
先来看第一个问题,这个可以通过 MAT 在 dump 中继续寻找答案。



再来看看第二个问题,这就需要找一下源码了。首先 max-http-header-size 是 Spring Boot 定义的参数,查看 Spring Boot 代码可以看到这个参数对应 Tomcat 的 MaxHttpHeaderSize。





