内存映射文件,是由一个文件到一块内存的映射。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。
这是百度上面的解释,相对来说内存映射文件还是非常重要的,但相对于小型项目来说可能完全用不到。
NIO提供了一种将文件映射到内存的方法进行IO操作,他它比常规基于流的IO快很多,以下是几个流之间的比较:
package com.demo; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.FileChannel; /** * 把400万条数据输入到文件中并且取出来 对比io和nio的效率 * 当数据量过大的时候采用内存映射文件进行优化处理 * * */ public class Demo { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub long start = System.currentTimeMillis(); int count = 400_00000; String path = "e:\\temp_cache_tmp"; String path2 = "e:\\temp_nio.tmp"; String path3 = "e:\\temp_nio_mem.tmp"; IoWrite(path, count); long end = System.currentTimeMillis(); System.out.println("io写入时间" + (end - start)); start = System.currentTimeMillis(); IoRead(path, count); end = System.currentTimeMillis(); System.out.println("io读取时间" + (end - start)); start = System.currentTimeMillis(); NioWrite(path2, count); end = System.currentTimeMillis(); System.out.println("nio写入时间" + (end - start)); start = System.currentTimeMillis(); NioRead(path2, count); end = System.currentTimeMillis(); System.out.println("nio读取时间" + (end - start)); start = System.currentTimeMillis(); NioMemeryWrite(path3, count); end = System.currentTimeMillis(); System.out.println("nio内存映射文件写入" + (end - start)); start = System.currentTimeMillis(); NioMemeryRead(path3, count); end = System.currentTimeMillis(); System.out.println("nio内存映射文件读取" + (end - start)); } /* * 内存映射文件 读取 * */ public static void NioMemeryRead(String path, int count) { FileChannel fc = null; try { fc = new FileInputStream(path).getChannel(); IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).asIntBuffer(); while(ib.hasRemaining()) { ib.get(); } if (fc !=null) { fc.close(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //内存映射文件进行写入数据 public static void NioMemeryWrite(String path, int count) { FileChannel fc = null;; try { fc = new RandomAccessFile(path, "rw").getChannel(); IntBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, 0, count*4).asIntBuffer(); for (int i = 0; i < count; i++) { ib.put(i); } if (fc !=null) { fc.close(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /* * 普通io进行读取 * */ public static void IoRead(String path, int count) throws IOException { File file = new File(path); DataInputStream dis = null; try { dis = new DataInputStream(new BufferedInputStream( new FileInputStream(file))); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (int i = 0; i < count; i++) { dis.readInt(); } dis.close(); } /* * 普通io进行读操作 */ public static void IoWrite(String path, int count) throws IOException { File f = new File(path); if (!f.exists()) { try { f.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } DataOutputStream dos = null; try { dos = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(f))); for (int i = 0; i < count; i++) { dos.writeInt(i); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (dos != null) { dos.close(); } } public static int byte2int(byte b1, byte b2, byte b3, byte b4) { return ((b1 & 0xff) << 24) | ((b2 & 0xff) << 16) | ((b3 & 0xff) << 18) | (b4 & 0xff); } public static byte[] int2byte(int res) { byte[] targets = new byte[4]; targets[3] = (byte) (res & 0xff); targets[2] = (byte) ((res >> 8) & 0xff); targets[1] = (byte) ((res >> 16) & 0xff); targets[0] = (byte) ((res >>> 24) & 0xff); return targets; } /* * 采用Nio进行读取 */ public static void NioRead(String path, int count) { File file = new File(path); FileInputStream fin = null; try { fin = new FileInputStream(file); FileChannel fc = fin.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(count * 4); fc.read(byteBuffer); fc.close(); byteBuffer.flip(); while (byteBuffer.hasRemaining()) { byte2int(byteBuffer.get(), byteBuffer.get(), byteBuffer.get(), byteBuffer.get()); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /* * 采用Nio进行写 */ public static void NioWrite(String path, int count) throws IOException { File file = new File(path); FileOutputStream fout = null; try { fout = new FileOutputStream(file); FileChannel fileChannel = fout.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(4 * count); for (int i = 0; i < count; i++) { byteBuffer.put(int2byte(i)); } byteBuffer.flip(); fileChannel.write(byteBuffer); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } fout.close(); } }结果如下
总体来看Nio的读取和写入能力相对很快,在这里我的缓存设置比较大是能够完全读取和写入数据的大小,所以效率非常之快,在实践中可能效率要比这个要低一点,但是总体来说使用NIO的内存映射文件相对比其他方式都要快一个数量级