介绍完 java的内存模型之后,我们再说一个特别的东西:
直接内存
直接内存是一种特殊的内存分配方式,它允许Java程序直接通过操作系统的本地内存分配接口来分配内存,而不受Java堆的限制。
最常见的就是I/O操作 读写文件的时候
如果没有直接内存,通常在进行文件读取或写入时,数据会先被读取到堆内存中,然后再从堆内存写入到文件中。
具体地说,在传统的IO操作中,比如使用 FileInputStream 和 FileOutputStream 进行文件读写,数据会通过Java堆内存来传递。当从文件读取数据时,数据会首先被读取到堆内存的字节数组中,然后可以对字节数组进行操作。同样,当将数据写入文件时,数据也需要先被写入到堆内存的字节数组中,然后再通过IO操作写入到文件中。
这种方式需要在Java堆内存和操作系统之间进行频繁的数据拷贝,尤其是在大量数据的读写场景下,会导致性能和效率的下降。而使用直接内存则可以避免这种拷贝过程,数据可以直接在操作系统的本地内存和文件之间进行传输,提高了IO操作的效率和性能。
现在有了直接内存这个东西 我们就可以避免大量的文件数据 占用 堆内存, 我们仅仅在堆上面创建一个 buffer对象 ,而这个对象读写数据的时候 直接通过直接内存 与 系统的文件内存 进行交互,这样根本不占用堆内存空间
这样就带来了很大的优势:
无需通过Java堆分配:
直接内存不受Java堆大小的限制,可以通过操作系统的本地内存分配接口直接分配,因此不会导致Java堆空间的不足。
减少内存拷贝:
在进行I/O操作时,通常需要将数据从Java堆拷贝到内核空间,然后再进行I/O操作。使用直接内存可以避免这一拷贝过程,提高了数据传输的效率。
避免GC的影响:
直接内存的分配和释放通常由操作系统管理,不受Java虚拟机的垃圾回收器影响,因此可以减少垃圾回收的开销,提高程序的性能。
内存映射文件:
直接内存还可以用于内存映射文件(Memory-mapped Files)操作,这种方式可以将文件直接映射到内存中,可以提高文件的读写效率。
来两个常见的案例:
// 分配直接内存
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
// 从文件读取数据到直接内存
try (FileChannel channel = FileChannel.open(Paths.get("file.txt"), StandardOpenOption.READ)) {
channel.read(buffer);
} catch (IOException e) {
e.printStackTrace();
}
// 从直接内存读取数据
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
// 创建SocketChannel
SocketChannel channel = SocketChannel.open();
// 连接远程服务器
channel.connect(new InetSocketAddress("example.com", 80));
// 分配直接内存
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
// 从网络读取数据到直接内存
channel.read(buffer);
// 从直接内存写入数据到网络
buffer.flip();
channel.write(buffer);