OutOfDirectMemoryError是Netty堆外内存溢出的明确信号,需通过日志used/max比对、jcmd native_memory验证、leakDetectionLevel=paranoid定位未释放ByteBuf,并显式配置-XX:MaxDirectMemorySize与Netty内存池参数闭环调优。

OutOfDirectMemoryError 是 Spring Cloud Gateway 等基于 Netty 的网关服务在高吞吐场景下典型的堆外内存(Direct Memory)溢出表现。它不是 JVM 堆的问题,而是操作系统层面的 native 内存分配失败——Netty 用 Unsafe.allocateMemory 或 mmap 申请的直接内存超限了。2026 年大厂面试问这个,重点看你会不会分层定位、精准归因、闭环验证,而不是只会加 -XX:MaxDirectMemorySize。
看报错,先确认是不是真爆了堆外内存
别一看到 OutOfDirectMemoryError 就调参。先抓证据:
- 检查错误日志中明确的内存使用量,例如
used: 8568993562, max: 8589934592→ 已用约 8.0G,上限约 8.1G,确实逼近极限 - 查当前 JVM 的
-XX:MaxDirectMemorySize设置:ps -ef | grep java | grep MaxDirectMemorySize;没显式配置则默认等于-Xmx(JDK 8+) - 用
jcmd <pid> VM.native_memory summary查 committed direct memory,和报错数值比对是否一致
查 Netty 的内存池配置与实际行为
Gateway 默认用 PooledByteBufAllocator,但它的行为受多个隐式参数影响:
-
io.netty.allocator.numHeapArenas/numDirectArenas:默认是 CPU 核数 × 2,每 arena 管理固定 chunk,arena 过多会抬高基础开销 -
io.netty.allocator.pageSize(默认 8KB)、maxOrder(默认 11 → chunk=8KB×2¹¹=16MB):单个 arena 的 chunk 大小影响内存碎片和保留量 - 如果启用了
-Dio.netty.noPreferDirect=true或被某些客户端强制走 heap buffer,反而可能绕过 direct 分配,需结合流量特征判断
建议在启动参数中显式约束:
-Dio.netty.allocator.numDirectArenas=4 \ -Dio.netty.allocator.maxOrder=9 \ -XX:MaxDirectMemorySize=2g
排查泄漏点:不是分配多,而是释放不及时
堆外内存泄漏往往表现为 used 持续上涨不回落,即使 QPS 稳定。常见根因:
-
响应体未完全消费:下游服务返回大响应(如 5MB JSON),但 Gateway 转发时因超时、熔断或客户端断连,导致
ByteBuf没被release(),Netty 的ResourceLeakDetector默认只 warn,需开PARANOID级别:-Dio.netty.leakDetectionLevel=paranoid -
自定义 Filter 中缓存 ByteBuf:比如做请求体重放(retry)时,用
buffer.retain()但忘了release() -
WebClient 或 Reactor Netty 配置不当:
HttpClient.option(ChannelOption.SO_RCVBUF, 64 * 1024)这类 socket 缓冲区设置过大,会额外占用 direct memory
用 jcmd <pid> VM.native_memory detail 对比两次采样,重点关注 Internal 和 Direct 区域增长;再配合 jstack 查是否有大量 PooledUnsafeDirectByteBuf 在 WAITING 状态,常指向未释放链路。
生产必须做的三件事
-
强制设上限:
-XX:MaxDirectMemorySize=2g(根据机器内存和并发压测结果定,别留“余量”) -
打开泄露检测:
-Dio.netty.leakDetectionLevel=advanced(线上用 advanced,开发用 paranoid) -
监控指标落地:通过 Micrometer + Prometheus 暴露
reactor.netty.memory.used.direct和reactor.netty.memory.max.direct,告警阈值设为 85%
不设 MaxDirectMemorySize 就像开车不系安全带——不出事时感觉不到,出事就是致命的。

















