Skip to content

由 max-http-header-size 引起的 OOM

🏷️ Spring Boot

在对服务做压力测试时发现内存消耗非常大,而且一旦开始压测,内存消耗增速非常快,很快就 OOM 了。这个项目因为之前一直请求量很少,所以没有出过问题。经老板提示说之前另一个项目也遇到过类似的问题,当时是由于一个请求头部的配置设置过大导致的。

看了下服务的配置文件,确实有一个 server.max-http-header-size 的配置被设置为了 40485760 (大约是 38.6MB),而在 Spring Boot 中这个配置默认为 8KB。(不清楚为什么要设置这么大,对于 Header 来说 8KB 完全够用了)

java
/**
 * Maximum size of the HTTP message header.
 */
private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8);

经调查,在 Http11InputBuffer 初始化时分配了 max-http-header-size 配置指定大小的缓冲区。[1]

java
void init(SocketWrapperBase<?> socketWrapper) {

    wrapper = socketWrapper;
    wrapper.setAppReadBufHandler(this);

    int bufLength = headerBufferSize +
            wrapper.getSocketBufferHandler().getReadBuffer().capacity();
    if (byteBuffer == null || byteBuffer.capacity() < bufLength) {
        byteBuffer = ByteBuffer.allocate(bufLength);
        byteBuffer.position(0).limit(0);
    }
}

分析了通过 Spring Boot Admin 下载下来的 dump 文件,占用内存最大的 byte[] 确实是在 Http11InputBuffer.SocketInputBuffer 中创建的。

上面截图中的大小是我把 max-http-header-size 设置为 102,400,000 时的数据。

由于这个配置之前是写在代码的配置文件中的,暂时在 Nacos 中添加同名配置项以覆盖代码中的配置。

yaml
server:
  max-http-header-size: 8KB

修改配置后貌似不会自动生效,需要重启下服务。

另外,还有一个同名的配置 server.tomcat.max-http-header-size ,这个配置项已经废弃了,应使用 server.max-http-header-size 配置项。如果两个都配置了的话,server.tomcat.max-http-header-size 配置优先。

java
@SuppressWarnings("deprecation")
private DataSize determineMaxHttpHeaderSize() {
  return (this.serverProperties.getTomcat().getMaxHttpHeaderSize().toBytes() > 0)
    ? this.serverProperties.getTomcat().getMaxHttpHeaderSize()
    : this.serverProperties.getMaxHttpHeaderSize();
}

  1. 踩坑记录-maxHttpHeaderSize 配置 - 内存都去哪儿 ↩︎