Java中Direct Memory通过ByteBuffer.allocateDirect()向OS申请堆外内存,创建轻量DirectByteBuffer句柄并用Unsafe分配物理内存,受-XX:MaxDirectMemorySize限额控制,分配后清零并注册Cleaner。

Java 中直接内存(Direct Memory)的堆外分配,核心是通过 ByteBuffer.allocateDirect() 触发 JVM 向操作系统申请本地内存,不经过 Java 堆,也不受 GC 即时管理。
分配方式:allocateDirect 是唯一标准入口
调用 ByteBuffer.allocateDirect(n) 会完成两件事:
- 在 Java 堆中创建一个
DirectByteBuffer实例(仅几十字节,含元数据和地址指针) - 通过
Unsafe.allocateMemory(n)调用本地方法,向 OS 申请n字节连续的物理内存 - 该内存地址被保存在 DirectByteBuffer 对象内部,后续读写都通过这个地址操作
分配前的限额检查不可跳过
JVM 每次分配前都会校验全局限额,由参数 -XX:MaxDirectMemorySize 控制:
- 未显式设置时,默认值 ≈
-Xmx(JDK 8 及以后通常取堆最大值) - 若已用堆外内存 + 当前申请量 > 限额,直接抛
OutOfMemoryError: Direct buffer memory - 限额检查发生在
Bits.reserveMemory()中,是同步阻断式判断
底层依赖 Unsafe,不走 new 或堆分配路径
DirectByteBuffer 不是普通 Java 对象的堆内实例化流程:
立即学习“Java免费学习笔记(深入)”;
- 它不通过
new DirectByteBuffer()构造(该构造器为 package-private,禁止外部调用) - 不经过类加载、堆空间分配、对象头初始化等常规步骤
- 真正数据存储完全在堆外,JVM 仅维护一个轻量级 Java 对象作为“句柄”
注意:allocateDirect 不等于立即可用
分配成功后还需完成初始化:
- 调用
memset(addr, 0, n)清零内存区域(防止残留数据) - 注册 Cleaner 对象,绑定到该 DirectByteBuffer 实例上,为后续异步释放做准备
- 此时内存才真正可读写;若中间任一环节失败(如 OS 内存不足),会回滚并抛异常


















