概述
直接内存(Direct Memory)是在Java中使用较为底层的内存管理方式,它通过Java的NIO包中的ByteBuffer类来实现,让Java能够在堆外分配内存。这种方式通常用于需要大量、频繁的IO操作的场景,因为它可以减少在Java堆和本地堆之间复制数据的次数,从而提高性能。以下是关于直接内存的一些分析和代码示例。
不用直接内存

使用直接内存

直接内存的特点
- 常见于 NIO 操作时,用于数据缓冲区,提高IO性能
- 分配回收成本较高,但读写性能高
- 不受JVM 内存回收管理(系统分配的内存,不受JVM管理)
代码演示
在Java中使用直接内存需要通过ByteBuffer.allocateDirect()方法进行内存分配。以下是一个简单的代码示例:
import java.nio.ByteBuffer;
public class DirectMemoryTest {
public static void main(String[] args) {
// 分配直接内存
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
// 使用直接内存
directBuffer.put((byte) 123); // 示例:写入数据
directBuffer.flip(); // 转换为读模式
byte value = directBuffer.get(); // 示例:读取数据
System.out.println(“读取的数据为: ” + value);
// 提示JVM进行垃圾收集,注意:这不会立即回收直接内存,直到 directBuffer 不可达后才会开始回收直接内存
System.gc();
}
}
NIO缓冲区分析
NIO的缓冲区是一块可以写入数据,然后可以从中读取数据的内存区域。Buffer是一个抽象类,它最常用的类型是ByteBuffer。除了ByteBuffer,还有CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer和DoubleBuffer等。
当通过 allocateDirect() 方法创建直接缓冲区时,Java会在操作系统的本地堆中分配内存。由于避开了JVM堆,因此提高了效率,尤其是在大量数据读写的情况下。但是需要注意,直接缓冲区的创建和销毁的成本较高,不适用于生命周期短暂的情况。
直接内存垃圾回收
在Java中,可以通过调用ByteBuffer的cleaner() 方法来获取一个 Cleaner 对象,然后通过它来手动释放直接内存。示例如下:
import sun.misc.Cleaner;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
public class DirectMemoryExample {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); // 分配直接内存
// 获取Unsafe实例
try {
Field field = Unsafe.class.getDeclaredField(“theUnsafe”);
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
// 获取直接内存地址
long address = unsafe.getLong(byteBuffer, byteBuffer.position());
// 创建Cleaner实例,并注册清理任务
Cleaner cleaner = Cleaner.create(byteBuffer, () -> {
// 在此处执行直接内存释放操作
unsafe.freeMemory(address);
});
// 当ByteBuffer对象变为不可达时,Cleaner会自动执行释放操作
// 为了演示,我们在这里显式地让ByteBuffer变为不可达
byteBuffer = null;
System.gc(); // 建议JVM执行垃圾回收
} catch (Exception e) {
e.printStackTrace();
}
}
}
在这个示例中,当 ByteBuffer 对象不再被引用时,Cleaner 对象会调用指定的 Runnable 来执行直接内存的释放操作。这种方式虽然不是由Java虚拟机的垃圾回收器来管理直接内存,但是可以帮助确保直接内存得到及时释放,避免内存泄漏。
注意事项
使用直接内存需要注意几个关键点:
- 合理大小:分配太多直接内存可能会导致系统级别的内存不足。
- 释放资源:使用完毕后应及时释放直接内存,虽然Java会最终释放,但不及时释放可能会造成内存泄露。
- 内存映射文件:直接内存通常与内存映射文件(Memory-Mapped File)配合使用,适用于有大量数据读写磁盘的场景。
通过正确使用直接内存,可以在某些场景下显著提升Java应用程序的性能。然而,也要谨慎管理,避免内存泄漏和其他资源管理错误。
