直接内存溢出本质是堆外内存耗尽,非GC管理区域,需通过-XX:NativeMemoryTracking、JFR及框架泄漏检测定位ByteBuffer.allocateDirect()未释放或配置不当问题。

直接内存溢出(java.lang.OutOfMemoryError: Direct buffer memory)本质是堆外内存被耗尽,不是 GC 能管的区域,所以常规堆内存分析工具(如 jmap、MAT)看不到它。排查关键在于确认谁在大量申请 ByteBuffer.allocateDirect(),又没及时释放,或配置限制太紧。
看报错和 JVM 启动参数
先确认是否真由直接内存引起:错误信息必须明确含 Direct buffer memory。再检查启动时是否设置了 -XX:MaxDirectMemorySize,比如:
- 没设该参数 → 默认值等于
-Xmx(堆最大值),容易误判为堆问题 - 设了但值过小(如
-XX:MaxDirectMemorySize=16m)→ 小流量就可能触发 - 用了 Netty、Kafka 客户端、高性能文件读写等组件 → 它们默认重度依赖 direct buffer
监控和定位分配源头
直接内存不走 GC,但分配行为可被追踪。推荐组合使用:
- 启用 JVM 本地内存跟踪:
-XX:NativeMemoryTracking=detail,然后用jcmd <pid> VM.native_memory summary查看 direct memory 实时占用量 - 开启 JFR(Java Flight Recorder)录制,筛选事件
jdk.DirectBufferAllocation,能精准看到哪行代码、哪个线程、多大尺寸在频繁分配 - 对 Netty 等框架,开启其内部指标(如
-Dio.netty.leakDetection.level=paranoid)可捕获未释放的 direct buffer 泄漏点
检查常见泄漏模式
多数直接内存 OOM 并非“用得多”,而是“没还上”。重点关注:
立即学习“Java免费学习笔记(深入)”;
-
ByteBuffer.allocateDirect()后未配合.cleaner().clean()(虽然通常靠 GC 触发,但 GC 不及时或对象长期存活会导致延迟释放) - NIO Channel 读写后,buffer 没调
.clear()或.flip()导致重复分配新 buffer - Netty 中
PooledByteBufAllocator配置不当(如maxOrder过高、chunk 大小不合理),或禁用池化却高频创建 unpooled buffer - 使用
MappedByteBuffer映射大文件后,未显式调用System.gc()(不推荐)或依赖 Cleaner,而系统负载高时 Cleaner 线程滞后
验证与修复建议
改完后别只看是否不崩,要验证释放是否及时:
- 压测时持续观察
jcmd ... VM.native_memory中Internal和Direct区域增长趋势是否平缓 - 用
jstack抓线程快照,搜DirectByteBuffer相关堆栈,确认高分配线程是否符合业务预期 - 临时加大
-XX:MaxDirectMemorySize只是掩耳盗铃;优先做代码层资源回收加固,再按需调参


















