server.max-http-header-size设置不当引发的线上OOM案例分析

问题现象

后台服务日志,大量报出如下异常,关键字:java.lang.OutOfMemoryError: Java heap space,问题指向:o.a.c.h.Http11NioProtocol [DirectJDKLog.java:175] Failed to complete processing of a request

问题定位

线上OOM后,dump内存快照,通过MAT工具进行分析,如下图所示,有多个200MB的Http11OutputBuffer对象存在,这很不合理。


排查相关配置后发现,server.max-http-header-size被设置为200MB。

问题解决

将server.max-http-header-size调整为1MB即可。

问题分析

Tomcat在构建Http11OutputBuffer对象时会通过maxHttpHeaderSize来设置HTTP message header的大小,其默认为8KB,并通过ByteBuffer.allocate(headerBufferSize)向内存申请,也就是说每接收到一个HTTP请求,都会向内存申请200MB的空间,所以,当请求并发上来以后,必然会导致OOM。

源码

org.apache.coyote.http11.AbstractHttp11Protocol类,构建Processor对象

java 复制代码
@Override
protected Processor createProcessor() {
    Http11Processor processor = new Http11Processor(this, adapter);
    return processor;
}

Http11Processor构建

java 复制代码
public Http11Processor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
    super(adapter);
    this.protocol = protocol;
    httpParser = new HttpParser(protocol.getRelaxedPathChars(),
            protocol.getRelaxedQueryChars());
    inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
            protocol.getRejectIllegalHeader(), httpParser);
    request.setInputBuffer(inputBuffer);
    // getMaxHttpHeaderSize默认为8KB,也可以通过server.max-http-header-size进行修改
    outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
    response.setOutputBuffer(outputBuffer);
    // Create and add the identity filters.
    inputBuffer.addFilter(new IdentityInputFilter(protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new IdentityOutputFilter());
    // Create and add the chunked filters.
    inputBuffer.addFilter(new ChunkedInputFilter(protocol.getMaxTrailerSize(),
            protocol.getAllowedTrailerHeadersInternal(), protocol.getMaxExtensionSize(),
            protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new ChunkedOutputFilter());
    // Create and add the void filters.
    inputBuffer.addFilter(new VoidInputFilter());
    outputBuffer.addFilter(new VoidOutputFilter());
    // Create and add buffered input filter
    inputBuffer.addFilter(new BufferedInputFilter());
    // Create and add the gzip filters.
    //inputBuffer.addFilter(new GzipInputFilter());
    outputBuffer.addFilter(new GzipOutputFilter());
    pluggableFilterIndex = inputBuffer.getFilters().length;
}
java 复制代码
protected Http11OutputBuffer(Response response, int headerBufferSize) {
    this.response = response;
    // 内存申请
    headerBuffer = ByteBuffer.allocate(headerBufferSize);
    filterLibrary = new OutputFilter[0];
    activeFilters = new OutputFilter[0];
    lastActiveFilter = -1;
    responseFinished = false;
    outputStreamOutputBuffer = new SocketOutputBuffer();
}
相关推荐
追逐时光者17 分钟前
一款基于 .NET 开源、免费的数据库自治诊断平台
后端·.net
nice_lcj52018 分钟前
Java 并发容器入门:ConcurrentHashMap、CopyOnWriteArrayList、阻塞队列
java·面试·并发编程·阻塞队列·concurrenthashmap·copyonwritearraylist
微小的鱼29 分钟前
Linux的渲染与显示系统链路
android·java·linux
追逐时光者31 分钟前
让 GitHub Copilot 更懂你的 .NET 代码风格:一份精简实用的 Copilot Instructions
后端·.net
小满zs1 小时前
Go语言第十八章(架构设计)
后端·go
若亦_1 小时前
Java中关于this的三种用法
java·开发语言·intellij-idea
山甫aa1 小时前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web
软件技术NINI1 小时前
基于SpringBoot和Vue的博物馆预约平台管理系统的设计与实现
java·毕业设计·毕设
IT_陈寒2 小时前
Java开发实战:从入门到精通
前端·人工智能·后端
jktantan2 小时前
注解式脱敏:用 Jackson 的 ContextualSerializer 做字段级策略(一)
spring boot·后端·算法·spring cloud·系统架构