zoukankan      html  css  js  c++  java
  • 【NIO】MappedByteBuffer-内存映射文件 I/O

    操作系统会在负责执行映射,用于操作大文件

    java io操作中通常采用BufferedReader,BufferedInputStream等带缓冲的IO类处理大文件;java nio中引入了一种基于MappedByteBuffer操作大文件的方式,其读写性能极高

    FileChannel提供了map方法把文件映射到虚拟内存,通常情况可以映射整个文件,如果文件比较大,可以进行分段映射

    MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);   //fc-->FileChannel
    //map通过native函数map0完成文件的映射工作,返回DirectByteBuffer

      

     MappedByteBuffer的get方法最终通过DirectByteBuffer.get方法实现的

    public byte get() {
        return ((unsafe.getByte(ix(nextGetIndex()))));
    }
    public byte get(int i) {
        return ((unsafe.getByte(ix(checkIndex(i)))));
    }
    private long ix(int i) {
        return address + (i << 0);
    }
    

      

    map0()函数返回一个地址address,这样就无需调用read或write方法对文件进行读写,通过address就能够操作文件。底层采用unsafe.getByte方法,通过(address + 偏移量)获取指定内存的数据。

    1. 第一次访问address所指向的内存区域,导致缺页中断,中断响应函数会在交换区中查找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则从硬盘上将文件指定页读取到物理内存中(非jvm堆内存)
    2. 如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘的虚拟内存中

    性能:

    从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。
    但是通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为什么?

    • read()是系统调用,首先将文件从硬盘拷贝到内核空间的一个缓冲区,再将这些数据拷贝到用户空间,实际上进行了两次数据拷贝;
    • map()也是系统调用,但没有进行数据拷贝,当缺页中断发生时,直接将文件从硬盘拷贝到用户空间,只进行了一次数据拷贝。

    所以,采用内存映射的读写效率要比传统的read/write性能高

    总结:

      • MappedByteBuffer使用虚拟内存,因此分配(map)的内存大小不受JVM的-Xmx参数限制,但是也是有大小限制的。
      • 如果当文件超出1.5G限制时,可以通过position参数重新map文件后面的内容。
      • MappedByteBuffer在处理大文件时的确性能很高,但也存在一些问题,如内存占用、文件关闭不确定,被其打开的文件只有在垃圾回收的才会被关闭,而且这个时间点是不确定的
        javadoc中也提到:A mapped byte buffer and the file mapping that it represents remainvalid until the buffer itself is garbage-collected.*
  • 相关阅读:
    iOS基础
    iOS基础
    iOS基础
    iOS基础
    iOS基础
    iOS基础
    iOS基础
    简单DP + 高精
    高精度计算
    树 (tree)
  • 原文地址:https://www.cnblogs.com/itplay/p/11078559.html
Copyright © 2011-2022 走看看