直接缓冲区与非直接缓冲区别
非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
直接字节缓冲区可以通过调用此类的 allocateDirect() 工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
直接字节缓冲区还可以通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。该方法返回MappedByteBuffer 。 Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。
字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect() 方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。
非直接缓冲区案例
准备一个1.avi的文件,大小最好不低于100M
@Test
// 1.利用通道完成文件复制(非直接缓冲区)
public void test1() throws IOException { //11953 、3207、3337
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("e://1.avi");
FileOutputStream fos = new FileOutputStream("e://2.avi");
// ①获取到通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
// ②分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
while (inChannel.read(buf) != -1) {
buf.flip();// 切换到读取模式
outChannel.write(buf);
buf.clear();// 清空缓冲区
}
// 关闭连接
outChannel.close();
inChannel.close();
fos.close();
fis.close();
long endTime = System.currentTimeMillis();
System.out.println("非缓冲区:"+(endTime-startTime));
}
输出结果
直接缓冲区案例
@Test
// 使用直接缓冲区完成文件的复制(內存映射文件) //428、357
public void test2() throws IOException {
long startTime = System.currentTimeMillis();
FileChannel inChannel = FileChannel.open(Paths.get("e://1.avi"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("e://2.avi"), StandardOpenOption.READ, StandardOpenOption.WRITE,
StandardOpenOption.CREATE);
// 映射文件
MappedByteBuffer inMapperBuff = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMapperBuff = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
// 直接对缓冲区进行数据读写操作
byte[] dst = new byte[inMapperBuff.limit()];
inMapperBuff.get(dst);
outMapperBuff.put(dst);
outChannel.close();
inChannel.close();
long endTime = System.currentTimeMillis();
System.out.println("直接缓冲区内存映射文件耗时:"+(endTime-startTime));
}
输出结果:
通过上面的两个案例,我们可以得出一个结论,直接缓冲区比非直接缓冲区处理文件要快
分散读取与聚集写入
分散读取(scattering Reads):将通道中的数据分散到多个缓冲区中
聚集写入(gathering Writes):将多个缓冲区的数据聚集到通道中
案例
@Test
public void test3() throws IOException{
long startTime = System.currentTimeMillis();
RandomAccessFile raf1 = new RandomAccessFile("e://1.txt", "rw");
// 1.获取通道
FileChannel channel = raf1.getChannel();
// 2.分配指定大小的指定缓冲区
ByteBuffer buf1 = ByteBuffer.allocate(100);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
// 3.分散读取
ByteBuffer[] bufs = { buf1, buf2 };
channel.read(bufs);
for (ByteBuffer byteBuffer : bufs) {
// 切换为读取模式
byteBuffer.flip();
}
System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
System.out.println("------------------分算读取线分割--------------------");
System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
// 聚集写入
RandomAccessFile raf2 = new RandomAccessFile("e://2.txt", "rw");
FileChannel channel2 = raf2.getChannel();
channel2.write(bufs);
long endTime = System.currentTimeMillis();
System.out.println("分散读取与聚集写入时间:"+(endTime-startTime));
}
输出结果: