Java NIO学习篇之直接缓冲区和非直接缓冲区

16 篇文章 0 订阅
15 篇文章 1 订阅

定义

在这里插入图片描述
以上是书深入理解java虚拟机对直接内存的描述。直接缓冲区用的就是直接内存。

  • java nio字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则java虚拟机会尽最大努力直接在此缓冲区上执行本机的IO操作,也就是说,在每次调用基础操作系统的一个本机IO操作前后,虚拟机都会尽量避免将内核缓冲区内容复制到用户进程缓冲区中,或者反过来,尽量避免从用户进程缓冲区复制到内核缓冲区中。
  • 直接缓冲区可以通过调用该缓冲区类的allocateDirect(int capacity) 方法创建,此方法返回的缓冲区进行分配和取消分配所需的成本要高于非直接缓冲区。直接缓冲区的内容驻留在垃圾回收堆之外,因此他们对应用程序内存(JVM内存)需求不大。所以建议直接缓冲区要分配给那些大型,持久(就是缓冲区的数据会被重复利用)的缓冲区,一般情况下,最好仅在直接缓冲区能在程序性能带来非常明显的好处时才分配它们。
  • 直接缓冲区还可以通过FileCHannel的map()方法将文件区域映射到内存中来创建,该方法返回MappedByteBuffer。java平台的实现有助于通过JNI本地代码创建直接字节缓冲区,如果以上这些缓冲区中某个缓冲区实例指向的是不可访问的内存区域,则试图方法该区域不会更改缓冲区的内容,并且会在访问期间或者稍后的某个时间导致报出不确定性异常。
  • 字节缓冲区是直接缓冲区还是非直接缓冲区可以通过调用其isDIrect()方法来判断。
基于NIO的本地IO直接内存使用:

传统IO对文件数据进行读写的流程:
在这里插入图片描述
流程说明(以上是应用程序完成一次文件拷贝的流程):

  1. 应用进程发起一个读请求系统调用,然后进程切换到内核态。
  2. DMA把磁盘数据复制到内核缓冲区中。
  3. 内核把缓冲区数据复制到用户缓冲区中。
  4. 进程切换到用户态。
  5. 应用进程发起一个写请求系统调用,然后进程切换到内核态。
  6. 内核把用户缓冲区数据复制到内核缓冲区。
  7. DMA把内核缓冲区数据复制到磁盘上。
  8. 返回。

以上流程一共进行了四次上下文切换,四次数据拷贝。

使用mmap实现对传统文件IO优化。
mmap:通过把内核缓冲区和用户缓冲区映射在物理内存上映射为同一地址空间。这样就不用对数据进行复制了。
在这里插入图片描述
这个是传统的:
在这里插入图片描述
使用mmap后的IO大致流程:
在这里插入图片描述
数据拷贝次数从4次缩短到了两次。

相关API demo以及比较:详细api解释可以查看[Java NIO学习篇之通道FileChannel详解](https://blog.csdn.net/qq_40837310/article/details/106265506)


//使用直接缓冲区API进行一个700多M的文件进行拷贝
public static void testDirect(){
        try {
            long start = System.currentTimeMillis();
            FileChannel srcFileChannel = FileChannel.open(Paths.get("C:\\Users\\Yehaocong\\Desktop\\test\\95462017-1-64.flv"), StandardOpenOption.READ);
            FileChannel destFileChannel = FileChannel.open(Paths.get("C:\\Users\\Yehaocong\\Desktop\\test\\95462017-1-64-cp1.flv"),StandardOpenOption.CREATE,
                    StandardOpenOption.WRITE,StandardOpenOption.READ);
            MappedByteBuffer srcByteBuffer = srcFileChannel.map(FileChannel.MapMode.READ_ONLY,0,srcFileChannel.size());
            MappedByteBuffer descByteBuffer = destFileChannel.map(FileChannel.MapMode.READ_WRITE,0,srcFileChannel.size());
            descByteBuffer.put(srcByteBuffer);
            srcFileChannel.close();
            destFileChannel.close();
            System.out.println("直接缓冲区耗时:" + (System.currentTimeMillis()-start));
      } catch (IOException e) {
            e.printStackTrace();
        }
    }

public static void testSimpleIO(){
        try {
            long start = System.currentTimeMillis();
            FileChannel srcFileChannel = FileChannel.open(Paths.get("C:\\Users\\Yehaocong\\Desktop\\test\\95462017-1-64.flv"), StandardOpenOption.READ);
            FileChannel destFileChannel = FileChannel.open(Paths.get("C:\\Users\\Yehaocong\\Desktop\\test\\95462017-1-64-cp.flv"),StandardOpenOption.CREATE,
                    StandardOpenOption.WRITE,StandardOpenOption.READ);
            ByteBuffer byteBuffer = ByteBuffer.allocate((int) srcFileChannel.size());
            while (srcFileChannel.read(byteBuffer)!=-1){
                byteBuffer.flip();
                destFileChannel.write(byteBuffer);
                byteBuffer.clear();
            }


            srcFileChannel.close();
            destFileChannel.close();

            System.out.println("非缓冲区耗时:" + (System.currentTimeMillis()-start));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

执行结果:
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java中,可以使用缓冲区(Buffer)来接收数据。缓冲区是一个数组,用于存储数据。Java中提供了四种类型的缓冲区:ByteBuffer、CharBuffer、ShortBuffer和IntBuffer。其中,ByteBuffer是最常用的缓冲区类型。 下面是一个简单的示例,演示如何使用ByteBuffer创建缓冲区并接收数据: ``` import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class BufferExample { public static void main(String[] args) throws Exception { SocketChannel channel = SocketChannel.open(); channel.connect(new InetSocketAddress("www.example.com", 80)); ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = channel.read(buffer); while (bytesRead != -1) { buffer.flip(); while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } buffer.clear(); bytesRead = channel.read(buffer); } channel.close(); } } ``` 在上面的示例中,我们首先创建了一个SocketChannel,并连接到了一个远程服务器。然后,我们创建了一个ByteBuffer对象,并分配了1024个字节的空间。接着,我们使用SocketChannel的read()方法将数据读入到缓冲区中。如果读取成功,则返回读取的字节数;如果已经到达流的末尾,则返回-1。 接下来,我们使用flip()方法将缓冲区从写模式切换到读模式。然后,我们使用hasRemaining()方法检查是否还有剩余的数据可供读取。如果有,我们使用get()方法读取数据,并将其转换为字符打印出来。最后,我们使用clear()方法清空缓冲区,并再次调用read()方法读取更多的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值