直接内存(Direct Memory)是JVM堆外、由操作系统分配的本地内存,通过ByteBuffer.allocateDirect()申请,不受GC管理,但受-XX:MaxDirectMemorySize限制;常用于NIO、Netty等高性能IO场景,不当使用易引发堆外OOM。

直接内存(Direct Memory)不属于JVM堆,而是由操作系统本地内存分配,常用于NIO、Netty等高性能场景。它不受 -Xmx 限制,但受物理内存和 -XX:MaxDirectMemorySize 控制;未显式设置时,默认值等于 -Xmx(JDK 7+),若堆设为2G,直接内存上限也默认为2G——这点极易被忽略,导致堆外OOM。
直接内存的典型触发场景
不是所有 ByteBuffer 都走直接内存。只有 ByteBuffer.allocateDirect() 创建的对象才真正占用直接内存;而 ByteBuffer.allocate() 分配在堆内,GC 可回收。常见高风险场景包括:
- 高频创建 DirectByteBuffer(如短连接服务中每个请求都 new 一次)
- Netty 中未及时释放
PooledByteBufAllocator分配的池化缓冲区(尤其在异常路径下漏调.release()) - 使用
sun.misc.Unsafe或 JNI 手动分配 native memory,但未配套free - 某些数据库驱动(如 PostgreSQL 的 pgjdbc)内部使用 DirectBuffer 做零拷贝,连接数多且长连接未复用时易堆积
-XX:MaxDirectMemorySize 的作用与陷阱
该参数仅限制 JVM 主动管理的 DirectByteBuffer 总用量(即通过 java.nio.Bits.reserveMemory() 计数的部分),不约束其他 JNI/native 分配行为。关键点:
- 必须带单位(如
-XX:MaxDirectMemorySize=512m),不写单位会被当作字节处理(512 字节显然不合理) - 设为 0 表示禁止使用 DirectByteBuffer(但部分框架会 fallback 到 heap buffer,性能下降)
- 即使设置了该参数,若底层 native code 绕过 JVM 内存计数机制(如自定义 malloc),仍可能突破限制并触发 OS 级 OOM Killer
- 该值不会影响
Unsafe.allocateMemory等非标准分配,这类内存需开发者自行跟踪和释放
堆外 OOM 的识别与诊断方法
异常信息通常为:java.lang.OutOfMemoryError: Direct buffer memory,但有时仅表现为进程被系统 kill(dmesg | tail 可见 Out of memory: Kill process)。诊断步骤:
- 启动时加参数
-XX:+PrintGCDetails -XX:+PrintReferenceGC,观察 GC 日志中是否频繁出现Direct buffer memory相关提示 - 运行时用
jstat -gc <pid>查看CCU(Compressed Class Space Used)、MCU(Metaspace Capacity Used)无明显增长,但进程 RSS 持续飙升 → 堆外嫌疑大 - 用
native-memory tracking (NMT):启动加-XX:NativeMemoryTracking=detail,再执行jcmd <pid> VM.native_memory summary,重点关注Internal和Other区域增长 - Linux 下用
pmap -x <pid>或cat /proc/<pid>/maps | grep -i "anon\|heap" | wc -l辅助判断匿名映射页数量是否异常增多
实用调优建议
避免堆外 OOM 不是单纯加大限额,而是控制生命周期与资源复用:
- 优先使用池化方案:Netty 的
PooledByteBufAllocator、Apache Commons Pool 封装 DirectBuffer,显著降低分配/释放开销 - 确保
try-with-resources或显式.release()覆盖所有 DirectBuffer 使用路径,尤其注意异常分支 - 监控
java.nio.Buffer.totalCapacity()(需通过反射或 JMX 获取)作为间接指标,结合业务 QPS 做容量预警 - 对长期运行服务,定期触发
System.gc()并非推荐做法,但可配合-XX:+DisableExplicitGC彻底禁用,逼迫代码走正确释放路径 - 若确认是第三方库导致(如旧版 Hadoop、Spark),升级到已修复版本,或通过容器内存限制(cgroup v2 memory.max)兜底防崩

















