【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射
内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问。这种解决办法能大大简化修改文件的代码。
fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大;也就是说,它还可以映射一个大文件的某个小片断。
MappedByteBuffer是ByteBuffer的子类,因此它具备了ByteBuffer的所有方法,但新添了force()将缓冲区的内容强制刷新到存储设备中去、load()将存储设备中的数据加载到内存中、isLoaded()位置内存中的数据是否与存储设置上同步。这里只简单地演示了一下put()和get()方法,除此之外,你还可以使用asCharBuffer( )之类的方法得到相应基本类型数据的缓冲视图后,可以方便的读写基本类型数据。
内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。
内存映射文件 I/O 是通过使文件中的数据神奇般地出现为内存数组的内容来完成的。这其初听起来似乎不过就是将整个文件读到内存中,但是事实上并不是这样。一般来说,只有文件中实际读取或者写入的部分才会送入(或者 映射 )到内存中。
内存映射并不真的神奇或者多么不寻常。现代操作系统一般根据需要将文件的部分映射为内存的部分,从而实现文件系统。Java 内存映射机制不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。
尽管创建内存映射文件相当简单,但是向它写入可能是危险的。仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
(1)了解内存映射的最好方法是使用例子。在下面的例子中,我们要将一个 FileChannel
(它的全部或者部分)映射到内存中。为此我们将使用 FileChannel.map()
方法。下面代码行将文件的前 1024 个字节映射到内存中:
public class UseMappedFile
{
static private final int start = 0;
static private final int size = 1024; static public void main( String args[] ) throws Exception {
RandomAccessFile raf = new RandomAccessFile( "usemappedfile.txt", "rw" );
FileChannel fc = raf.getChannel(); MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE,
start, size ); mbb.put( 0, (byte)97 );
mbb.put( 1023, (byte)122 );
for (int i = start; i < size; i++) {
System.out.print((char) mbb.get(i));
}
raf.close();
}
}
运行结果:
(2)该程序创建了一个128Mb的文件,如果一次性读到内存可能导致内存溢出,但这里访问好像只是一瞬间的事,这是因为,真正调入内存的只是其中的一小部分,其余部分则被放在交换文件上。这样你就可以很方便地修改超大型的文件了(最大可以到2 GB)。注意,Java是调用操作系统的"文件映射机制"来提升性能的。
public class LargeMappedFiles {
static int length = 0x8000000; // 128 Mb public static void main(String[] args) throws Exception {
// 为了以可读可写的方式打开文件,这里使用RandomAccessFile来创建文件。
FileChannel fc = new RandomAccessFile("test.dat", "rw").getChannel();
//注意,文件通道的可读可写要建立在文件流本身可读写的基础之上
MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);
//写128M的内容
for (int i = 0; i < length; i++) {
out.put((byte) 'x');
}
System.out.println("Finished writing");
//读取文件中间6个字节内容
for (int i = length / 2; i < length / 2 + 6; i++) {
System.out.print((char) out.get(i));
}
fc.close();
}
}
运行结果:
Finished writing
xxxxxx
(3)java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用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.
转自:http://langgufu.iteye.com/blog/2107023
【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射的更多相关文章
- java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射
java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用MappedByteBuffer. Mapped ...
- Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析
1. 在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区 ...
- Java NIO(四)文件通道
文件通道 通道是访问I/O服务的导管,I/O可以分为广义的两大类:File I/O和Stream I/O.那么相应的,通道也有两种类型,它们是文件(File)通道和套接字(Socket)通道.文件通道 ...
- Python文件(File)及读写操作及生成器yield
open函数在内存中创建缓存区,将磁盘上的内容复制到此处.文件内容读入到文件对象缓冲区后,文件对象将缓冲区视为非常大的列表,其中每个元素都有一个索引.文件对象按字节(大约每个字符)来对文件对象缓冲区索 ...
- java nio通过ByteBuffer输出文件信息
1.通过ByteBuffer的get()方法每次读取一个字节转换成char类型输出. fc = new FileInputStream("src/demo20/data.txt") ...
- java nio读取和写入文件
读取 package com.test; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputS ...
- Delphi- ini文件的读写操作
一.读INI文件示例 procedure TForm1.FormCreate(Sender: TObject); Var MyIni :Tinifile; glAppPath :string; beg ...
- ios 简单的plist文件读写操作(Document和NSUserDefaults)
最近遇到ios上文件读写操作的有关知识,记录下来,以便以后查阅,同时分享与大家. 一,简单介绍一下常用的plist文件. 全名是:Property List,属性列表文件,它是一种用来存储串行化后的对 ...
- Java使用FFmpeg处理视频文件指南
Java使用FFmpeg处理视频文件指南 本文主要讲述如何使用Java + FFmpeg实现对视频文件的信息提取.码率压缩.分辨率转换等功能: 之前在网上浏览了一大圈Java使用FFmpeg处理音视频 ...
随机推荐
- Android开发2——创建测试项目
一.创建普通Android项目 二.在AndroidManifest.xml添加两个配置 <?xml version="1.0" encoding="utf-8 ...
- activiti表
act_re_deployment #部署对象表 act_re_prodef #流程定义表 act_ge_bytearray #资源文件表 act_ge_property #主键生成策略表 ac ...
- Racket 版本的 24 点实现
Racket 版本的 24 点实现 #lang racket ; Author: woodfox ; Date: Oct 11, 2014 ; ==================== 1. Non- ...
- [na]代理arp导致的问题(路由卷)
已过期... 一 理论概述 \ 二 实验 实验一:代理arp在nat中的作用(实验发现一下是错的) 实验二.代理arp导致的问题 pc访问服务器想让走路由器(写32bit静态路由),右边的R arp ...
- java动态代理的两种方法
动态代理,有两种情况,第一种是有接口的情况下,你可以选择为jdk自带的动态代理的方式来编写程序,但你想要为一个实在的类编写动态代理的方式的话,这时候就必须选择一些开源的lib包,如cglib包,同时还 ...
- 【转】linux configure报错configure: error: C++ preprocessor “/lib/cpp” fails sanity 的解决办法
/lib/cpp fails sanity check的解决 在某些软件的时候,运行./configure 会报错,错误提示为: configure: error: C++ preprocessor ...
- C++笔记 3
1.数组是自动分配空间,指针要手工分配空间(int *p = new int;) 2.在Unix上,程序出现段错误的时候,系统会生成core 文件,会把出现错误的那一刻的程序镜像保存在此文件中 3.结 ...
- web编码
1各种编码 A .1 html编码 -HTML标签 this.Response.Write(this.Server.HtmlEncode("<h1>的作用将文本设置为标题样式! ...
- flume sourcetype avro http
flume 当sourcetype=http的时候可以用 curl -X POST -d '[{"headers" :{"APPSTORE" : " ...
- mybatis深入学习
最近做的一个活可以让我深入学习一下现在比较流行的ORM框架:mybatis/ibatis的内部原理,SQL的拦截,解析,dataSource和JDBC中做一些额外的事情.如果有可能的话想造一个比较简单 ...