美文网首页
3.NIO直接缓冲区与非直接缓冲区

3.NIO直接缓冲区与非直接缓冲区

作者: xialedoucaicai | 来源:发表于2018-05-15 11:24 被阅读0次

非直接缓冲区,缓冲区建立在JVM内存中,实际读写数据时,需要在OS和JVM之间进行数据拷贝,如下图:

非直接缓冲区

为什么不直接让磁盘控制器把数据送到用户空间的缓冲区呢?这样做有几个问题。首先,硬件通常不能直接访问用户空间。其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色

直接缓冲区,缓冲区建立在受操作系统管理的物理内存中,OS和JVM直接通过这块物理内存进行交互,没有了中间的拷贝环节,如下图:

直接缓冲区

所有现代操作系统都使用虚拟内存。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件RAM)内存地址。这样做好处颇多,总结起来可分为两大类: 1. 一个以上的虚拟地址可指向同一个物理内存地址。 2. 虚拟内存空间可大于实际可用的硬件内存。
前一节提到,设备控制器不能通过 DMA 直接存储到用户空间,但通过利用上面提到的第一项,则可以达到相同效果。把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样,DMA 硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区(见图1-3)。

图 1-3. 内存空间多重映射

优点:速度更快,效率更高
缺点:①创建直接缓冲区将会有更多消耗②数据进入直接缓冲区后,后续写入磁盘等操作就完全由操作系统决定了,不受我们控制
什么时候用:缓冲区要长时间使用(数据本身需要长时间在内存
OR 缓冲区复用率很高),或者大数据量的操作(大文件才能体现出速度优势)。
如何使用

  1. 通过ByteBuffer.allocateDirect(),创建直接缓冲区
  2. 通过内存映射文件的方式
  3. 使用通道直接传输

下面以2G大小的文件,对比一下


非直接缓冲区完成文件复制

public void testChannel() throws Exception{
        long begin = System.currentTimeMillis();
        
        FileInputStream fis = new FileInputStream("1.zip");
        FileOutputStream fos = new FileOutputStream("2.zip");
        
        //获取通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();
        
        //创建Buffer
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        
        //将数据写入byteBuffer
        while(inChannel.read(byteBuffer) != -1){
            //转换模式
            byteBuffer.flip();
            //将byteBuffer中的数据读取到outChannel
            //注意这里的byteBuffer是有position limit属性的,write时候只会写position->limit之间的数据
            //所以,即使clear()并未真正清空buffer,这里也不会把上次的数据写入的
            outChannel.write(byteBuffer);
            //清空byteBuffer
            byteBuffer.clear();
        }
        
        outChannel.close();
        inChannel.close();
        fos.close();
        fis.close();
        
        System.out.println(System.currentTimeMillis() - begin);
    }

直接缓冲区完成文件复制
与上述代码一样,只是将创建缓冲区的方式改成ByteBuffer.allocateDirect()
内存映射文件方式
①原视频中直接将1G左右的文件一次性通过mapBuffer进行传输,这样比较的结果可能不准确,因为非直接缓冲区一次只读取1024个字节,所以我这里也改成了一次读1024个字节
②我用的是2G的文件,本来想试试一次读2G和多次读差别多大,结果直接报堆溢出,不是都直接操作物理内存了嘛,咋还跟JVM堆有关,这可能涉及到更深的内容,而且NIO重点也不在这块儿,所以这里就暂将此问题挂起,不深入研究了

public void testChannel2() throws Exception{
    long begin = System.currentTimeMillis();
    
    //另一种方式获取Channel
    FileChannel inChannel = FileChannel.open(Paths.get("1.zip"), StandardOpenOption.READ);
    FileChannel outChannel = FileChannel.open(Paths.get("3.zip"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    
    //内存映射文件
    MappedByteBuffer inMapBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
    MappedByteBuffer outMapBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
    
    //直接对映射缓冲区进行读写,不需要ByteBuffer
    //这里不能直接传2g,否则会报错堆溢出,要分开传
    byte [] tempBytes = new byte[1024];
        while(inMapBuffer.hasRemaining()){
            int shouldReadLength = inMapBuffer.remaining() > 1024?1024:inMapBuffer.remaining();
            //写入bytes
            inMapBuffer.get(tempBytes,0,shouldReadLength);
            //从bytes读出到outMapBuffer
            outMapBuffer.put(tempBytes,0,shouldReadLength);
        }
        
        
    outChannel.close();
    inChannel.close();
    
    System.out.println(System.currentTimeMillis() - begin);
}

通道直接传输
只有FileChannel有这个方式,不需要借助缓冲区

public void testChannel3() throws Exception{
    long begin = System.currentTimeMillis();

    FileChannel inChannel = FileChannel.open(Paths.get("1.zip"), StandardOpenOption.READ);
    FileChannel outChannel = FileChannel.open(Paths.get("3.zip"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    
    //这里传2g的也不会报堆溢出
    inChannel.transferTo(0, inChannel.size(), outChannel);
    //outChannel.transferFrom(inChannel, 0, inChannel.size());
    
    outChannel.close();
    inChannel.close();

    System.out.println(System.currentTimeMillis() - begin);
}

到这里应该能明白NIO与IO的第一个区别了吧:

NIO是面向缓冲区,基于通道进行IO操作,能以更加高效的方式进行文件的读取操作

相关文章

  • 3.NIO直接缓冲区与非直接缓冲区

    非直接缓冲区,缓冲区建立在JVM内存中,实际读写数据时,需要在OS和JVM之间进行数据拷贝,如下图: 为什么不直接...

  • 11.18

    Java 中,直接缓冲区与非直接缓冲器有什么区别?非直接缓冲区:通过allocate()分配缓冲区,将缓冲区建立在...

  • Java NIO(3) - 直接与非直接缓冲区

    3.5 直接与非直接缓冲区 3.5.1 基本介绍 字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则...

  • Java 零拷贝 、缓冲区

    这两部分知识体系暂时没有联系起来,先记录以下 一、缓冲区——直接缓冲与非直接缓冲 1.1 非直接缓冲区(堆缓冲区)...

  • 缓冲区

    首先讲一下,缓冲区和非缓冲区,所谓的非缓冲区就是就是通过键盘输入,然后直接在屏幕中显示,比如是就是打游戏的时候,通...

  • NIO复制文件的三种方式对比

    方式一:利用通道完成文件的复制(非直接缓冲区) 运行三次的时间分别为:用通道完成文件的复制(非直接缓冲区)耗时为1...

  • 缓冲区

    直接缓冲区 为什么要用直接缓冲区? 因为,操作系统进行 I/O 操作的目标内存区域必须是连续的字节序列。(因此只有...

  • iOS如何实现为UIImageView添加圆角(当前屏幕渲染)

    当前屏幕渲染绘制相对离屏渲染,GPU直接在当前显示的屏幕缓冲区中进行图形渲染,不需要提前另开缓冲区,也不需要缓冲区...

  • less 命令

    直接使用语法less [参数] 文件 参数说明: -b <缓冲区大小> 设置缓冲区的大小 -e 当文件显示结束后,...

  • OpenGL模板缓冲区与模板测试(转)

    OpenGL模板缓冲区与模板测试 帧缓冲区有许多缓冲区构成,这些缓冲区大致分为: 颜色缓冲区:用于绘图的缓冲区,它...

网友评论

      本文标题:3.NIO直接缓冲区与非直接缓冲区

      本文链接:https://www.haomeiwen.com/subject/spqqdftx.html