直接缓冲区与非直接缓冲区别
分散读取与聚集写入
非直接缓冲区
通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中!
案例展示
这里提供一个名为Python1,大小为397.1MB的zip文件;
现分别用直接缓冲区和非直接缓冲区对上面文件进行文件复制,看哪个缓冲区耗时最短,效率更高?
代码示例
public class NoStraightChannel {
public static void main(String[] args) throws IOException {
//利用通道完成文件的复制(非直接缓冲区)
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("/Users/yswKnight/Desktop/Python1.zip");
FileOutputStream fos = new FileOutputStream("/Users/yswKnight/Desktop/Python2.zip");
//1.获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
//2.分配制定大小的缓冲区
ByteBuffer allocate = ByteBuffer.allocate(1024);
System.out.println(inChannel.read(allocate));
while(inChannel.read(allocate)!=-1){
//切换为读取数据
allocate.flip();
//3.将缓冲区中的数据写入通道中
outChannel.write(allocate);
allocate.clear();
}
inChannel.close();
outChannel.close();
fis.close();
fos.close();
long end=System.currentTimeMillis();
System.out.println("非直接缓冲区完成文件复制所耗时:"+(end-start));
}
}
运行结果
直接缓冲区
通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率!
案例展示
代码示例
public class Channel {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
//使用直接缓冲区完成文件的复制(内存映射文件)
FileChannel inChannel = FileChannel.open(Paths.get("/Users/yswKnight/Desktop/Python1.zip"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("/Users/yswKnight/Desktop/Python2.zip"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//内存映射文件
MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//直接对缓冲区进行数据的读写操作
byte[] bytes = new byte[inMap.limit()];
inMap.get(bytes);
outMap.put(bytes);
inChannel.close();
outChannel.close();
long end=System.currentTimeMillis();
System.out.println("直接缓冲区完成文件复制所耗时:"+(end-start));
}
}
运行结果
可以多运行几遍,看平均值!
总结:很明显,直接缓冲区比非直接缓冲区的效率要高出5倍之多!
字节缓冲区要么是直接的,要么是非直接的。
如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,
在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
直接字节缓冲区可以通过调用此类的 allocateDirect() 工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。
直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。
所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
直接字节缓冲区还可以通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。该方法返回MappedByteBuffer 。 Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。
如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。
字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect() 方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。
分散读取与聚集写入
分散读取(scattering Reads):
将通道中的数据分散到多个缓冲区中
聚集写入(gathering Writes):
将多个缓冲区的数据聚集到通道中
代码示例
public class GatheringChannel {
public static void main(String[] args) throws IOException {
RandomAccessFile rw = new RandomAccessFile("test.txt", "rw");
//1.获取通道
FileChannel channel = rw.getChannel();
//2.分配指定大小的指定缓冲区
ByteBuffer allocate1 = ByteBuffer.allocate(100);
ByteBuffer allocate2 = ByteBuffer.allocate(1024);
//3.分散读取
ByteBuffer[] bufs={allocate1,allocate2};
channel.read(bufs);
for(ByteBuffer buffer:bufs){
//切换为读取模式
buffer.flip();
}
System.out.println("----------------分散读取allocate1缓冲区读100个字节-------------------");
System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
System.out.println("----------------分散读取allocate2缓冲区读1024个字节-------------------");
System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
//聚集写入
RandomAccessFile rw2 = new RandomAccessFile("test2.txt", "rw");
FileChannel Channel2 = rw2.getChannel();
Channel2.write(bufs);
//执行成功后会新生成一个test2.txt文件,里面的内容跟test.txt相似!
}
}
运行结果
--------------------- 本文来自 詠聖wK 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/yswKnight/article/details/79339848?utm_source=copy
原文地址:https://www.cnblogs.com/super-admin/p/9729082.html