java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用MappedByteBuffer

MappedByteBuffer是java nio引入的文件内存映射方案,读写性能极高。NIO最主要的就是实现了对异步操作的支持。其中一种通过把一个套接字通道(SocketChannel)注册到一个选择器(Selector)中,不时调用后者的选择(select)方法就能返回满足的选择键(SelectionKey),键中包含了SOCKET事件信息。这就是select模型。
    SocketChannel的读写是通过一个类叫ByteBuffer(java.nio.ByteBuffer)来操作的.这个类本身的设计是不错的,比直接操作byte[]方便多了. ByteBuffer有两种模式:直接/间接.间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存 (byte[]).但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即 MappedByteBuffer,文件映射.
     先中断一下,谈谈操作系统的内存管理.一般操作系统的内存分两部分:物理内存;虚拟内存.虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换". MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer.MappedByteBuffer 只是一种特殊的 ByteBuffer ,即是ByteBuffer的子类。 MappedByteBuffer 将文件直接映射到内存(这里的内存指的是虚拟内存,并不是物理内存)。通常,可以映射整个文件,如果文件比较大的话可以分段进行映射,只要指定文件的那个部分就可以。

三种方式:
              FileChannel提供了map方法来把文件影射为内存映像文件: MappedByteBuffer map(int mode,long position,long size); 可以把文件的从position开始的size大小的区域映射为内存映像文件,mode指出了 可访问该内存映像文件的方式:READ_ONLY,READ_WRITE,PRIVATE.                    
a. READ_ONLY,(只读): 试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException.(MapMode.READ_ONLY)
 b. READ_WRITE(读/写): 对得到的缓冲区的更改最终将传播到文件;该更改对映射到同一文件的其他程序不一定是可见的。 (MapMode.READ_WRITE)
c. PRIVATE(专用): 对得到的缓冲区的更改不会传播到文件,并且该更改对映射到同一文件的其他程序也不是可见的;相反,会创建缓冲区已修改部分的专用副本。 (MapMode.PRIVATE)

三个方法:

a. fore();缓冲区是READ_WRITE模式下,此方法对缓冲区内容的修改强行写入文件
b. load()将缓冲区的内容载入内存,并返回该缓冲区的引用
c. isLoaded()如果缓冲区的内容在物理内存中,则返回真,否则返回假

三个特性:

调用信道的map()方法后,即可将文件的某一部分或全部映射到内存中,映射内存缓冲区是个直接缓冲区,继承自ByteBuffer,但相对于ByteBuffer,它有更多的优点:

a. 读取快
b. 写入快
c. 随时随地写入

下面来看代码:

  1. package study;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.nio.ByteBuffer;
  5. import java.nio.MappedByteBuffer;
  6. import java.nio.channels.FileChannel;
  7. public class MapMemeryBuffer {
  8. public static void main(String[] args) throws Exception {
  9. ByteBuffer byteBuf = ByteBuffer.allocate(1024 * 14 * 1024);
  10. byte[] bbb = new byte[14 * 1024 * 1024];
  11. FileInputStream fis = new FileInputStream("e://data/other/UltraEdit_17.00.0.1035_SC.exe");
  12. FileOutputStream fos = new FileOutputStream("e://data/other/outFile.txt");
  13. FileChannel fc = fis.getChannel();
  14. long timeStar = System.currentTimeMillis();// 得到当前的时间
  15. fc.read(byteBuf);// 1 读取
  16. //MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
  17. System.out.println(fc.size()/1024);
  18. long timeEnd = System.currentTimeMillis();// 得到当前的时间
  19. System.out.println("Read time :" + (timeEnd - timeStar) + "ms");
  20. timeStar = System.currentTimeMillis();
  21. fos.write(bbb);//2.写入
  22. //mbb.flip();
  23. timeEnd = System.currentTimeMillis();
  24. System.out.println("Write time :" + (timeEnd - timeStar) + "ms");
  25. fos.flush();
  26. fc.close();
  27. fis.close();
  28. }
  29. }
  30. 运行结果:
  31. 14235
  32. Read time :24ms
  33. Write time :21ms
  34. 我们把标注1和2语句注释掉,换成它们下面的被注释的那条语句,再来看运行效果。14235
  35. Read time :2ms
  36. Write time :0ms

可以看出速度有了很大的提升。MappedByteBuffer的确快,但也存在一些问题,主要就是内存占用和文件关闭等不确定问题。被MappedByteBuffer打开的文件只有在垃圾收集时才会被关闭,而这个点是不确定的。在javadoc里是这么说的:A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself  is garbage-collected.
这里提供一种解决方案:

  1. AccessController.doPrivileged(new PrivilegedAction() {
  2. public Object run() {
  3. try {
  4. Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
  5. getCleanerMethod.setAccessible(true);
  6. sun.misc.Cleaner cleaner = (sun.misc.Cleaner)
  7. getCleanerMethod.invoke(byteBuffer, new Object[0]);
  8. cleaner.clean();
  9. } catch (Exception e) {
  10. e.printStackTrace();
  11. }
  12. return null;
  13. }
  14. });

关于MappedByteBuffer资源释放问题

JDK1.4中加入了一个新的包:NIO(java.nio.*)。这个库最大的功能(我认为)就是增加了对异步套接字的支持。其实在 其他语言中,包括在最原始的SOCKET实现(BSD SOCKET),这是一个早有的功能:异步回调读/写事件,通过选择器动态选择感兴趣的事件,等等。
先谈谈操作系统的内存管理。一般操作系统的内存分两部分:物理内存;虚拟内存。虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换"。
MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer。这是一个很好的设计,除了令人头疼的一点在后面会讲到。
java.lang.Object
   java.nio.Buffer
      java.nio.ByteBuffer
          java.nio.MappedByteBuffer
MappedByteBuffer是一个比较方便使用的类。其内容是文件的内存映射区域。映射的字节缓冲区是通过FileChannel.map 方法创建的。映射的字节缓冲区和它所表示的文件映射关系在该缓冲区本身成为垃圾回收缓冲区之前一直保持有效。此类用特定于内存映射文件区域的操作扩展 ByteBuffer 类。 这个类本身的设计是不错的,比直接操作byte[]方便多了。
ByteBuffer有两种模式:直接/间接。间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存(byte [])。但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即 MappedByteBuffer,文件映射。
在JDK API文档中这样描述的:
全部或部分映射的字节缓冲区可能随时成为不可访问的,例如,如果我们截取映射的文件。试图访问映射的字节缓冲区的不可访问区域将不会更改缓冲区 的内容,并导致在访问时或访问后的某个时刻抛出未指定的异常。因此强烈推荐采取适当的预防措施,以避免此程序或另一个同时运行的程序对映射的文件执行操作 (读写文件内容除外)。
MappedByteBuffer只能通过调用FileChannel的map()取得,再没有其他方式.但是令人奇怪的是,SUN提供了map()却没有提供unmap().这样会导致什么后果呢?
这样,问题就出现了。通过MappedByteBuffer实现文件复制功能非常容易,可以用以下方法来实现。
   //文件复制
   public void copyFile(String filename,String srcpath,String destpath)throws IOException {
    File source = new File(srcpath+"/"+filename);
    File dest = new File(destpath+"/"+filename);
     FileChannel in = null, out = null;
     try { 
      in = new FileInputStream(source).getChannel();
      out = new FileOutputStream(dest).getChannel();
      long size = in.size();
      MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);
      out.write(buf);
      in.close();
      out.close();
      source.delete();//文件复制完成后,删除源文件
     }catch(Exception e){
      e.printStackTrace();
     } finally {
      in.close();
      out.close();
     }
   }
但是如果要实现文件文件复制完成后,删除源文件,以上方法就有问题。因为在source.delete()时,会返回false,删除失败,主 要原因是变量buf仍然有源文件的句柄,文件处于不可删除状态。既然MappedByteBuffer是从FileChannel中map()出来的,为 什么它又不提供unmap()呢?SUN自己也没有讲清楚为什么。O'Reilly的<<Java NIO>>中说是因为"安全"的原因,但是到底unmap()会怎么不安全,作者也没有讲清楚。
在sun网站也有相应的BUG报告:bug id:4724038链接为http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038,但是sun自己不认为是BUG,而只是一个RFE(Request For Enhancement),有待改进。
好在有个叫bellomi的网友提出了一个解决方法,我也测试过,可以实现期望的功能。具体实现代码如下:
   public static void clean(final Object buffer) throws Exception {
         AccessController.doPrivileged(new PrivilegedAction() {
             public Object run() {
             try {
                Method getCleanerMethod = buffer.getClass().getMethod("cleaner",new Class[0]);
                getCleanerMethod.setAccessible(true);
                sun.misc.Cleaner cleaner =(sun.misc.Cleaner)getCleanerMethod.invoke(buffer,new Object[0]);
                cleaner.clean();
             } catch(Exception e) {
                e.printStackTrace();
             }
                return null;}});
         
}
不知道为什么SUN不提供ByteBuffer的派生。毕竟这是一个很实用的类,如果允许派生,那么我就可以操作的就不仅仅限于堆内存和文件了,我可以扩展到任何存储设备。

java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射的更多相关文章

  1. Java 字节流实现文件读写操作(InputStream-OutputStream)

    Java 字节流实现文件读写操作(InputStream-OutputStream) 备注:字节流比字符流底层,但是效率底下. 字符流地址:http://pengyan5945.iteye.com/b ...

  2. Java 字符流实现文件读写操作(FileReader-FileWriter)

    Java 字符流实现文件读写操作(FileReader-FileWriter) 备注:字符流效率高,但是没有字节流底层 字节流地址:http://pengyan5945.iteye.com/blog/ ...

  3. java文件读写操作类

    借鉴了项目以前的文件写入功能,实现了对文件读写操作的封装 仅仅需要在读写方法传入路径即可(可以是绝对或相对路径) 以后使用时,可以在此基础上改进,比如: 写操作: 1,对java GUI中文本框中的内 ...

  4. Golang: 常用的文件读写操作

    Go 语言提供了很多文件操作的支持,在不同场景下,有对应的处理方式,今天就来系统地梳理一下,几种常用的文件读写的形式. 一.读取文件内容 1.按字节读取文件 这种方式是以字节为单位来读取,相对底层一些 ...

  5. [转]Android - 文件读写操作 总结

     转自:http://blog.csdn.net/ztp800201/article/details/7322110 Android - 文件读写操作 总结 分类: Android2012-03-05 ...

  6. Kotlin入门(27)文件读写操作

    Java的文件处理用到了io库java.io,该库虽然功能强大,但是与文件内容的交互还得通过输入输出流中转,致使文件读写操作颇为繁琐.因此,开发者通常得自己重新封装一个文件存取的工具类,以便在日常开发 ...

  7. Kotlin入门-文件读写操作

    转 https://blog.csdn.net/aqi00/article/details/83241762 Java的文件处理用到了io库java.io,该库虽然功能强大,但是与文件内容的交互还得通 ...

  8. python里文件读写操作

    文件读写操作一种基本操作,但是里面也存在很多需要注意的问题,例如字符编码.内存缓冲.指针位置等等.如果忽视这些问题就会引起很多不必要的麻烦.简单来说,文件的读写分为几个过程: 打开文件,并定义操作文件 ...

  9. loadrunner 脚本开发-文件读写操作

    脚本开发-文件读写操作 by:授客 QQ:1033553122 函数说明 函数原型: size_t fwrite( const void *buffer, size_t size, size_t co ...

  10. loadrunder脚本篇——文件读写操作

     函数说明 函数原型: size_t fwrite( const void *buffer, size_t size, size_t count, FILE *file_pointer ); 参数说明 ...

随机推荐

  1. 【HDU2007】平方和与立方和

    http://acm.hdu.edu.cn/showproblem.php?pid=2007 垃圾水题 随便搜了几个公式(但我实际写的暴力...题目保证不爆int,说明n,m<=10^3) 1^ ...

  2. DFS Used%: NaN%问题

    一.问题描述: [root@master sbin]# hdfs dfsadmin -report Configured Capacity: 0 (0 B) Present Capacity: 0 ( ...

  3. Python3中使用PyMongo的方法详解

    前言 本文主要给大家介绍的是关于在Python3使用PyMongo的方法,分享出来供大家参考学习,下面话不多说了,来一起看看详细介绍: MongoDB存储 在这里我们来看一下Python3下Mongo ...

  4. 读取每行的数据,加入到list中

    有txt文件中,每行都有一个字符串或者数据,将每行的数据转换到一个list中 例如: 1 2 3 6 实现: f = open("test1.txt",'r') list1 = [ ...

  5. 用js实现的一个可拖动标签的例子

    先贴代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  6. J20170426-hm

    ジェネリクス Generics 泛型 バルーン balloon 气球 アングルブラケット Angle bracket 尖括号 プレースホルダ Placeholder 占位符

  7. 洛谷 - P2657 - windy数 - 数位dp

    https://www.luogu.org/problemnew/show/P2657 不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. 这道题是个显然到不能再显然的数位dp了. 来个 ...

  8. iOS中音频的录制与播放(本地音频文件的播放)

    iOS功能开发涉及到音频处理时,最常见的时进行录音,以及音频文件的播放.停止播放等的操作.在开发中还要避免同一个音频文件,或不同音频文件之间的处理,比如说正在播放A音频时,可以停止播放A音频,也可以播 ...

  9. yii1 compoment实现自己的db类

    突然发现yii1并没有实现mysql的读写分离默认配置,而用yii1的配置实现读写分离又很麻烦,所以我写了一个db的辅助类 首先我们需要配置一下一下辅助db的compoment类,yii的compom ...

  10. 优化 SQL 查询:如何写出高性能SQL语句

    1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条 ...