java nio 之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. 随时随地写入


package study;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MapMemeryBuffer {

    public static void main(String[] args) throws Exception {
        ByteBuffer byteBuf = ByteBuffer.allocate(1024 * 14 * 1024);
        byte[] bbb = new byte[14 * 1024 * 1024];
        FileInputStream fis = new FileInputStream("e://data/other/UltraEdit_17.00.0.1035_SC.exe");
        FileOutputStream fos = new FileOutputStream("e://data/other/outFile.txt");
        FileChannel fc = fis.getChannel();
        long timeStar = System.currentTimeMillis();// 得到当前的时间
        fc.read(byteBuf);// 1 读取
        //MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
        System.out.println(fc.size()/1024);
        long timeEnd = System.currentTimeMillis();// 得到当前的时间
        System.out.println("Read time :" + (timeEnd - timeStar) + "ms");
        timeStar = System.currentTimeMillis();
        fos.write(bbb);//2.写入
        //mbb.flip();
        timeEnd = System.currentTimeMillis();
        System.out.println("Write time :" + (timeEnd - timeStar) + "ms");
        fos.flush();
        fc.close();
        fis.close();
    }

}

运行结果:

14235
 Read time :24ms
 Write time :21ms

我们把标注1和2语句注释掉,换成它们下面的被注释的那条语句,再来看运行效果。

14235
 Read time :2ms
 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.
 

这里提供一种解决方案:

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(byteBuffer, new Object[0]);
       cleaner.clean();
     } catch (Exception e) {
       e.printStackTrace();
     }
     return null;
   }
 });



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您提供一个示例代码,将Java的FileChannel和MappedByteBuffer用于读写文件,并将读和写的代码分开。请参考以下示例: 首先,我们来看一下读取文件的代码: ```java import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class FileReader { public static void main(String[] args) { try { // 打开文件 RandomAccessFile file = new RandomAccessFile("input.txt", "r"); FileChannel channel = file.getChannel(); // 将文件映射到内存中 MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); // 读取文件内容 while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } // 关闭文件 channel.close(); file.close(); } catch (Exception e) { e.printStackTrace(); } } } ``` 接下来,我们来看一下写入文件的代码: ```java import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class FileWriter { public static void main(String[] args) { try { // 打开文件 RandomAccessFile file = new RandomAccessFile("output.txt", "rw"); FileChannel channel = file.getChannel(); // 将文件映射到内存中 MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1024); // 写入数据到缓冲区 String data = "Hello, World!"; buffer.put(data.getBytes()); // 刷新缓冲区到文件 buffer.force(); // 关闭文件 channel.close(); file.close(); } catch (Exception e) { e.printStackTrace(); } } } ``` 这两个示例代码分别用于读取和写入文件。您可以根据自己的需求进行修改和扩展。记得替换文件名和路径以适应您的实际情况。希望对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值