directbytebuffer通过绕过jvm堆、由os直接分配内存,使io时跳过用户态到内核态的数据拷贝,实现零拷贝;其底层调用unsafe.allocatememory()并绑定cleaner回收,配合transferto等方法可直接传递地址给native io函数,显著提升大流量io性能。

ByteBuffer.allocateDirect 创建的是直接内存(Direct Memory),它绕过 JVM 堆,由操作系统直接管理,因此在 IO 操作中能减少一次用户态到内核态的数据拷贝,提升性能。
为什么能绕过堆内存?
普通堆内缓冲区(ByteBuffer.allocate())的数据位于 JVM 堆中。进行 IO(如 Socket 写入或文件通道传输)时,JVM 必须先把堆内数据复制到本地内存(即内核空间可用的缓冲区),再交给 OS 发送——这叫“copy-in”。而 allocateDirect() 分配的内存本身就在本地内存(native memory),OS 可直接访问,跳过了复制步骤。
底层如何实现零拷贝加速?
DirectBuffer 在创建时会调用本地方法(如 Unsafe.allocateMemory())向操作系统申请内存,并通过 Cleaner 注册回收钩子。关键在于:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- FileChannel.transferTo() 或 SocketChannel.write() 等方法,遇到 DirectBuffer 时可直接传递内存地址给 native IO 函数(如 sendfile、writev)
- 避免了 JVM 堆 → 本地缓冲区 → 内核缓冲区 的两次拷贝,变成直接:本地内存 → 内核缓冲区
- 尤其在大块数据、高频 IO 场景(如网络服务器、消息队列)中,效果明显
使用时要注意什么?
直接内存不是免费的午餐,需权衡代价:
- 分配/释放比堆内存慢(涉及系统调用),不适合频繁创建销毁的小缓冲区
- 不受 GC 直接管理,依赖 Cleaner 异步回收,可能引发 OutOfMemoryError: Direct buffer memory
- 默认大小受限(可通过 -XX:MaxDirectMemorySize 调整,不设则约等于 -Xmx)
- 部分旧版 JDK 中,DirectBuffer 回收延迟可能导致内存泄漏假象
怎么确认用了 direct buffer?
可通过以下方式验证:
- buffer.isDirect() 返回 true
- JVM 参数加 -XX:+PrintGCDetails,GC 日志中会显示 DirectMemory 使用量
- JDK9+ 可用 JMX 的 BufferPoolMXBean 查看 direct 缓冲池统计
不复杂但容易忽略:只有配合支持 direct buffer 的 channel(如 FileChannel、SocketChannel)和 transfer 方法,才能真正发挥零拷贝优势;若用 get()/put() 手动读写,反而因额外边界检查和 unsafe 访问开销略慢于堆缓冲区。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










