什么是 Java 中的直接内存(堆外内存)?
什么是 Java 中的直接内存(堆外内存)?
回答重点
Java 中的直接内存(Direct Memory)是由操作系统分配的内存区域,它不受 JVM 堆内存管理限制(是堆外内存)。直接内存是通过 java.nio 包中的 ByteBuffer.allocateDirect() 方法分配的,它可以绕过 JVM 垃圾回收机制,直接与本地系统内存交互。
扩展知识
直接内存的优势
由于直接内存不需要在堆上进行分配和复制数据,因此与操作系统的 I/O 操作(如文件读写、网络传输)时可以减少一次复制,提升性能,在文件读写和网络传输场景直接内存有很大的优势。
性能优化策略
- 使用缓存机制:可以缓存 ByteBuffer.allocateDirect() 分配的缓冲区,减少频繁的直接内存分配。
- 合理设置 JVM 参数:堆外内存不归 JVM 设置的堆大小限,但是可以通过设置 -XX:MaxDirectMemorySize 来设置直接内存的最大使用量,避免不必要的内存消耗。
直接内存和堆内存的区别
- 分配位置:堆内存由 JVM 管理,受 GC 控制;直接内存由操作系统分配,使用本地内存。
- 访问速度:直接内存的访问速度在特定场景下更快,因为减少了堆内存到本地内存的复制过程。
- 回收机制:堆内存由垃圾回收器自动回收,而直接内存的回收需要通过调用
ByteBuffer的cleaner方法进行清理。
直接内存使用示例
在 Java 中可以利用 Unsafe 类和 NIO 类库使用直接内存。
例如利用 NIO 的 ByteBuffer.allocateDirect(1024) 即可分配得到一个直接内存。
简单示例如下:
1 | import java.nio.ByteBuffer; |
注意最后一行释放内存的 cleaner 。因为垃圾回收器无法直接管理堆外内存,所以 JVM 在创建 ByteBuffer 的时候,在堆内存储了这个对象的指针,然后注册了一个关联的 cleaner(清理器)。
可以看到,源码里面绑定了一个 cleaner:
而这个 cleaner 是个虚引用(详细看面试鸭关于虚引用的面试题):
如果 JVM 检测到没有对象关联 ByteBuffer,说明这个堆外内存已经成为了垃圾,此时 ByteBuffer 会被回收,然后 cleaner 会被加入到引用队列中,之后会就会被触发其 clean 接口,然后清理堆外内存。
Comments