-
传统的文件i/o是通过用户进程发布read()和write()系统调用来传输数据的。为了在内核空间与系统空间之间移动数据,一次以上的拷贝操作几乎是少不了的,因为在文件系统页与用户缓冲区之间往往没有一一对应关系,但是内存映射i/o是一种特殊的i/o操作,它允许用户进程最大限度地利用面向页的i/o特性。
-
通过内存映射文件机制访问一个文件会比使用常规方法读写高效得多,甚至比使用通道的效率都高,因为不需要做明确的系统调用,并且操作系统的虚拟内存会自动缓存内存页,不会消耗jvm的堆内存。
-
内存映射i/o的好处
1.用户进程把文件数据当作内存,无需发布read和write操作。
2.用户进程对内存映射空间的操作会让映射空间的内容自动更新。
3.数据总是页对齐的,无需执行缓冲区拷贝。
4.大型文件使用映射,无需耗费大量内存即可进行数据拷贝。 -
在FileChannel上调用map方法,会创建一个由磁盘文件支持的虚拟内存映射并在这块内存外部封装一个MapperByteBuffer对象。
-
map()方法返回的MapperByteBuffer对象的行为在多数方面类似于一个基于内存的缓冲区,之不过该对象的数据元素存储在磁盘上的一份文件中。调用get()方法可以从中获取数据,该数据是对文件当前内容的反映(当你建立映射以后,外部进程对该文件的改变也会立即得到反应)。相似的,对文件的修改对于其它阅读者也是可见的。
-
对于包含索引以及其它需要频繁引用或更新内容的文件会因此受益非常多。同时可以结合文件锁来保护关键区域并控制事务原子性。
map()方法创建文件映射需要 三个参数,position参数指定锁定区域的起始位置,size参数指定锁定区域的大小,不过映射文件的范围不应该超出文件的实际大小,因为映射文件会被增大去匹配设定的大小。第三个参数mode指的是映射模式,read——only、read-write。它们表示你希望获取的映射只读还是允许修改映射的文件。并且映射模式将受到通道的访问权限所限制。 -
特别的,MapMode.PRIVATE表示想要一个写时拷贝的映射,这就意味着你对与此映射的修改都不会对底层文件产生影响,尽管如此,我们也必须以rw权限打开文件建立的映射模式才允许使用put()方法。
-
一旦一个映射建立起来之后,它将持续有效,直到MapperByteBuffer对象被垃圾收集器回收,映射缓冲区不会绑定到创建它们的通道上,通过关闭了,映射还会继续存在,只有丢弃缓冲区本身才会破坏映射。
-
MapperByteBuffer.foecr()方法同channel.force方法类似,都是讲对缓冲区的更改强制更新到磁盘里。但是这对于read_only和private是无效的。
public static long readFile() throws Exception { long startTime = System.currentTimeMillis(); FileInputStream fis = new FileInputStream("jetty- 9.3.10.tar.gz"); FileOutputStream fos = new FileOutputStream("jetty-9.3.10.copy.tar.gz"); byte[] b = new byte[1024 * 50]; int len = 0; while ((len = fis.read(b)) != -1) { fos.write(b, 0, len); } fis.close(); fos.close(); long endTime = System.currentTimeMillis(); return endTime - startTime; } public static long readFileWithNIO() throws Exception { long startTime = System.currentTimeMillis(); FileInputStream fis = new FileInputStream("jetty-9.3.10.tar.gz"); FileOutputStream fos = new FileOutputStream("jetty-9.3.10.copy.tar.gz"); FileChannel fisChannel = fis.getChannel(); FileChannel fosChannel = fos.getChannel(); ByteBuffer readBuffer = ByteBuffer.allocate(1024 * 50); int len = fisChannel.read(readBuffer); while (len != -1) { readBuffer.flip(); fosChannel.write(readBuffer); readBuffer.clear(); len = fisChannel.read(readBuffer); } long endTime = System.currentTimeMillis(); fis.close(); fos.close(); return endTime - startTime; } public static long readFileWithMapper() throws Exception { long startTime = System.currentTimeMillis(); FileInputStream fis = new FileInputStream("jetty-9.3.10.tar.gz"); FileOutputStream fos = new FileOutputStream("jetty-9.3.10.copy.tar.gz"); FileChannel fisChannel = fis.getChannel(); FileChannel fosChannel = fos.getChannel(); MappedByteBuffer mbb = fisChannel.map(FileChannel.MapMode.READ_ONLY, 0, fisChannel.size()); fosChannel.write(mbb); long endTime = System.currentTimeMillis(); fis.close(); fos.close(); return endTime - startTime; } public static void main(String[] args) throws Exception { System.out.println("BIO time = " + readFile()); System.out.println("NIO time = " + readFileWithNIO()); System.out.println("MAPPER TIME = " + readFileWithMapper()); }
`
网友评论