java nio MappedByteBuffer 文件映射
2014-04-26 19:35
561 查看
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 com.zero.nio.test; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class Test implements INIOTest{ public void copyFileMapped(String oldPath,String newPath) throws IOException{ long length=0; RandomAccessFile raf=new RandomAccessFile(oldPath , "r"); FileChannel fcr=raf.getChannel(); length=fcr.size(); //返回要读取文件的映射内存区块 MappedByteBuffer mbb=fcr.map(FileChannel.MapMode.READ_ONLY, 0, length); ByteBuffer buffer=mbb.get(new byte[(int)length]); //要写入的文件 RandomAccessFile raw=new RandomAccessFile(newPath, "rw"); FileChannel fcw=raw.getChannel(); MappedByteBuffer mbbw=fcw.map(FileChannel.MapMode.READ_WRITE, 0, length); for(int i=0;i<length;i++){ mbbw.put(i,buffer.get(i)); } fcw.close(); fcr.close(); raf.close(); raw.close(); /** * MappedByteBuffer是java平台共享内存的实现,把硬盘虚拟为内存, * 主要用于进程间共享数据,所以在进程没有退出前文件是不允许删除的。 * 一个映射的字节缓冲区和文件映射,它代表仍然有效,直到缓冲本身是垃圾收集。 */ raf=null; raw=null; System.gc(); try { //等待垃圾回收 Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } public void copyFileNIO(String oldPath,String newPath) throws IOException{ FileInputStream fis=new FileInputStream(oldPath); FileOutputStream fos=new FileOutputStream(newPath); //获取输入输出通道 FileChannel fcin=fis.getChannel(); FileChannel fout=fos.getChannel(); //创建缓冲区 ByteBuffer buffer=ByteBuffer.allocate(1024); while(true){ //clear方法重设缓冲区,使它可以接受读入的数据 buffer.clear(); int len=fcin.read(buffer); if(len==-1){ break; } //写模式转换成读模式。该限制设置为当前的位置然后位置设置为零。如果标记定义然后丢弃。 //flip方法让缓冲区可以将新读入的数据写入另一个通道 buffer.flip(); fout.write(buffer); } fcin.close(); fout.close(); } public void copyFile(String oldPath,String newPath) throws IOException{ FileInputStream fis=new FileInputStream(oldPath); FileOutputStream fos=new FileOutputStream(newPath); BufferedInputStream bis=new BufferedInputStream(fis); BufferedOutputStream bos=new BufferedOutputStream(fos); byte[] buffer=new byte[1024]; int len=0; while((len=bis.read(buffer))!=-1){ bos.write(buffer,0,buffer.length); } bis.close(); bos.close(); } public static void main(String agrs[]){ INIOTest test1=(INIOTest)TestHandler.newInstance(new Test()); try { test1.copyFileMapped("D:\\20140415.txt","D:\\test.txt"); test1.copyFileNIO("D:\\20140415.txt","D:\\test1.txt"); test1.copyFile("D:\\20140415.txt","D:\\test2.txt"); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } 可以看出速度有了很大的提升。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; } }); |
相关文章推荐
- Java NIO之内存映射文件——MappedByteBuffer
- Java Nio中的三种内存映射缓冲区---MappedByteBuffer
- java NIO之MappedByteBuffer
- 内存映射文件:MappedByteBuffer
- 【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射
- Java NIO之MappedByteBuffer,高效文件内存映射
- Atitit.病毒木马的快速扩散机制原理nio 内存映射MappedByteBuffer
- NIO MappedByteBuffer读大文件并统计出现次数最多的TOP K个单词
- 【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射
- MappedByteBuffer的映射内存的释放
- 极大提高java I/O效率的方法:谈谈MappedByteBuffer
- 使用MappedByteBuffer读取大文件(1G以上)和释放MappedByteBuffer的资源
- MappedByteBuffer高速缓存文件、RandomAccessFile随机访问
- RandomAccessFile、FileChannel、MappedByteBuffer读写文件
- MappedByteBuffer读写文件
- java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射
- FileInputStream RandomAccessFile BufferedReader MappedByteBuffer 大文件数据读取效率测试
- RandomAccessFile、FileChannel、MappedByteBuffer读写文件
- java nio 之MappedByteBuffer,高效文件/内存映射
- 为何要在Java中使用内存映射文件(Memory Mapped File)或者MappedByteBuffer