java nio 使用直接内存(direct memory)主要通过 bytebuffer.allocatedirect() 方法创建,该方式分配的内存位于堆外,由 jvm 直接管理,避免了 java 堆与操作系统内核缓冲区之间的数据拷贝。要正确使用它,开发者需实例化一个 direct bytebuffer,进行读写操作后,在不再需要时通过 cleaner.clean() 或显式调用 free()(若使用了 unsafe api)来释放内存,防止内存泄漏。

如何分配和释放直接内存
分配直接内存只需调用 ByteBuffer.allocateDirect(1024),返回的对象内部持有一个指向堆外内存的引用。释放方面,由于 ByteBuffer 本身是 Java 对象,当它被垃圾回收时,JVM 会通过 Cleaner 机制异步释放底层内存。但在高并发或长生命周期场景下,建议手动触发清理或使用池化技术,确保内存及时归还给系统。

import java.nio.ByteBuffer;
import sun.misc.Unsafe;
public class DirectMemoryDemo {
public static void main(String[] args) throws Exception {
// 1. 分配直接内存
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
// 2. 写入数据
buffer.put("Hello".getBytes());
buffer.flip();
// 3. 读取数据
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println(new String(data));
// 4. 手动清理(可选,通常依赖GC)
// buffer.cleaner().clean(); // Java 9+ 已移除 cleaner 公开访问
}
}
直接内存与堆内缓冲区的性能差异
直接内存的优势在于零拷贝(Zero-Copy),特别是在涉及 Native I/O 操作时,如 FileChannel.transferTo(),可以直接将堆外内存映射到内核缓冲区,减少一次从用户态到内核态的数据复制。相比之下,堆内缓冲区(Heap Buffer)在传输时需要先复制到直接缓冲区,再传给内核,增加了 CPU 开销和延迟。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型

import java.io.FileInputStream;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
public class ZeroCopyDemo {
public static void main(String[] args) throws Exception {
FileInputStream in = new FileInputStream("source.txt");
FileChannel channel = in.getChannel();
// 使用直接内存进行零拷贝传输
ByteBuffer directBuffer = ByteBuffer.allocateDirect(8192);
channel.read(directBuffer);
// 堆内缓冲区则需要额外拷贝
ByteBuffer heapBuffer = ByteBuffer.allocate(8192);
channel.read(heapBuffer); // 内部可能涉及间接拷贝
channel.close();
in.close();
}
}
直接内存溢出异常的处理
当直接内存耗尽时,JVM 会抛出 java.lang.OutOfMemoryError: Direct buffer memory。这通常发生在未正确释放 Direct ByteBuffer 或分配量超过 -XX:MaxDirectMemorySize 限制时。排查此类问题时,应检查是否有长期持有的 Direct Buffer 引用,或使用 jcmd 和 jmap -direct 工具监控直接内存使用情况。此外,确保所有 Direct Buffer 在使用完毕后被置为 null 或放入池中复用,可有效避免泄漏。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










