direct memory溢出排查需先确认异常栈含“direct buffer memory”,再用-xx:nativememorytracking=detail监控internal区增长,结合mat分析directbytebuffer的gc roots定位泄漏源。

直接内存(Direct Memory)溢出排查难度高于堆内存,因为它不走GC流程、不受-Xmx控制,且错误日志特征隐蔽——常见报错是 java.lang.OutOfMemoryError: Direct buffer memory,但进程堆使用率可能很低,RSS(常驻内存)却持续上涨。
看日志确认是否为Direct Memory溢出
出现OOM时,先检查异常栈顶信息:
- 明确含 “Direct buffer memory” 字样 → 基本锁定为Direct Memory问题;
- 不含该字样但进程RSS暴涨、堆内存稳定、GC日志无异常 → 仍需怀疑Direct Memory或JNI本地内存;
- 注意区分:不是所有“OutOfMemoryError”都发生在堆上,Metaspace、Compressed Class Space、Direct Memory 都会抛同一类异常,仅靠“OOM”三字无法定性。
用JVM参数开启Direct Buffer监控
默认情况下JVM不记录Direct Buffer分配详情。需主动启用追踪:
- 启动时添加:-XX:NativeMemoryTracking=detail(JDK 8u60+ 支持);
- 运行中执行:jcmd
VM.native_memory summary scale=MB ,重点关注 Internal 和 Other 区域增长; - 若看到 Internal 持续上升(尤其伴随大量
java.nio.DirectByteBuffer分配),基本可确认是DirectBuffer累积所致。
查代码和框架是否隐式创建DirectBuffer
很多开发者没调用 ByteBuffer.allocateDirect(),却仍触发溢出——因为底层框架在“默默代劳”:
-
Netty:默认使用
PooledByteBufAllocator,但若未配置.direct(true)或池耗尽,会 fallback 到 unpooled direct buffer; - Jetty:高并发下默认启用 direct buffers 处理网络IO(尤其 HTTPS 场景),且部分版本存在 buffer 未及时清理的bug;
- gRPC / Spring WebFlux:基于Netty,同样受其buffer策略影响;
- 检查是否有自定义NIO Channel、FileChannel.map()、或者第三方库(如某些JSON解析器)内部使用了MappedByteBuffer或DirectByteBuffer。
限制与缓解:从JVM参数入手
Direct Memory虽不由GC管理,但JVM可通过参数设硬上限:
- 添加 -XX:MaxDirectMemorySize=512m(单位可为k/m/g),不设则默认≈-Xmx值;
- 配合 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 观察Full GC是否触发DirectBuffer清理(注意:只有当DirectByteBuffer对象被GC回收时,其背后native memory才会释放);
- 若频繁触发OOM,可临时加 -Dio.netty.noPreferDirect=true(针对Netty应用)强制走heap buffer,验证是否为direct引发。
关键点在于:Direct Memory泄漏本质是Java对象(DirectByteBuffer)没被回收,导致其持有的native内存无法释放。所以最终仍要回到堆内分析——用MAT打开Heap Dump,筛选 java.nio.DirectByteBuffer 实例数及retained heap,再追溯其GC Roots,就能定位谁在长期持有这些buffer。










