ByteBuffer.allocateDirect() 是最标准安全的直接内存分配方式,底层调用 Unsafe.allocateMemory() 向操作系统申请堆外内存,JVM 仅维护轻量对象,不参与 GC,适用于零拷贝场景;需按需设定容量、配合 -XX:MaxDirectMemorySize 限制总量,并避免循环高频创建。

用 ByteBuffer.allocateDirect() 就能分配直接内存,这是最标准、最安全的方式。
它底层调用 Unsafe.allocateMemory() 向操作系统申请一块堆外内存,JVM 只维护一个轻量对象(约 24 字节头),真实数据存在物理内存中,不参与 GC 扫描,地址稳定,适合零拷贝场景。
怎么正确分配直接内存
- 调用
ByteBuffer.allocateDirect(int capacity),传入所需字节数ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 1024); // 分配 1MB 直接内存
- 容量建议按需设定:太小会频繁切 buffer,太大易触发
OutOfMemoryError: Direct buffer memory - 避免在循环里高频创建(比如每请求都
allocateDirect(4096)),分配成本远高于堆内 buffer
分配后要注意什么
- 不要当成普通堆内 buffer 使用:没有 GC 自动兜底,也不能靠
System.gc()强制释放 - 必须配合 JVM 参数限制总量,否则容易 OOM
-XX:MaxDirectMemorySize=512m
默认值通常等于
-Xmx,但不设就可能耗尽本地内存 - 写入前记得
flip(),读完记得clear()或compact(),否则状态错乱导致数据丢失或越界
为什么不能手动释放
- 底层内存由
Cleaner(JDK9+)或sun.misc.Cleaner(JDK8)异步回收,绑定在DirectByteBuffer实例上 -
Cleaner.clean()会在对象被 GC 后调用Unsafe.freeMemory(),但时机不可控 - 切勿反射调用
freeMemory(),也不该重写finalize()—— 这些都破坏 JVM 内存管理契约
常见误用与替代思路
- ❌ 全局都换
allocateDirect:吞吐未必提升,反而增加分配开销和内存碎片 - ✅ 高频 I/O 场景(如 Netty 的 read/write、
FileChannel.transferTo)才值得用 - ✅ 短生命周期、小容量 buffer 仍推荐
ByteBuffer.allocate()(堆内) - ✅ 若需精细控制,可用 Netty 的
PooledByteBufAllocator复用 direct buffer,降低分配压力
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











