志在指尖
用双手敲打未来

Java内存映射,上G大文件轻松处置

内存映射文件(Memory-mappedFile),指的是将一段虚拟内存逐字节映射于一个文件,使得应用程序处置文件好像访问主内存(但在真正运用到这些数据前却不会耗费物理内存,也不会有读写磁盘的操作),这要比直接文件读写快几个数量级。
略微解释一下虚拟内存(很明显,不是物理内存),它是计算机系统内存管理的一种技术。像施了妖法一样使得应用程序以为它具有连续的可用的内存,实践上呢,它通常是被分隔成多个物理内存的碎片,还有局部暂时存储在外部磁盘存储器上,在需求时停止数据交流。
内存映射文件主要的用途是增加I/O性能,特别是针对大文件。关于小文件,内存映射文件反而会招致碎片空间的糜费,由于内存映射总是要对齐页边境,最小单位是4KiB,一个5KiB的文件将会映射占用8KiB内存,也就会糜费3KiB内存。java
java.nio包使得内存映射变得十分简单,其中的中心类叫做MappedByteBuffer,字面意义为映射的字节缓冲区。
01、运用MappedByteBuffer读取文件
假定如今有一个文件,名叫cmower.txt,里面的内容是:
缄默王二,一个有趣的程序员
PS:哎,改不了王婆卖瓜自卖自诩这个臭缺点了,由于文章被盗得都怕了。
这个文件放在/resource目录下,我们能够经过下面的办法获取到它:
ClassLoaderclassLoader=Cmower.class.getClassLoader();
Pathpath=Paths.get(classLoader.getResource(“cmower.txt”).getPath());
Path既能够表示一个目录,也能够表示一个文件,就像File那样——当然了,Path是用来取代File的。
然后,从文件中获取一个channel(通道,对磁盘文件的一种笼统)。
FileChannelfileChannel=FileChannel.open(path);
紧接着,调用FileChannel类的map办法从channel中获取MappedByteBuffer,此类扩展了ByteBuffer——提供了一些内存映射文件的根本操作办法。
MappedByteBuffermappedByteBuffer=fileChannel.map(mode,position,size);
略微解释一下map办法的三个参数。
1)mode为文件映射形式,分为三种:
MapMode.READ_ONLY(只读),任何试图修正缓冲区的操作将招致抛出ReadOnlyBufferException异常。
MapMode.READ_WRITE(读/写),任何对缓冲区的更改都会在某个时辰写入文件中。需求留意的是,其他映射同一个文件的程序可能不能立刻看到这些修正,多个程序同时停止文件映射的行为依赖于操作系统。
MapMode.PRIVATE(私有),对缓冲区的更改不会被写入到该文件,任何修正对这个缓冲区来说都是私有的。
2)position为文件映射时的起始位置。
3)size为要映射的区域的大小,必需是非负数,不得大于Integer.MAX_VALUE。
一旦把文件映射到内存缓冲区,我们就能够把里面的数据读入到CharBuffer中并打印出来。详细的代码示例如下。
CharBuffercharBuffer=null;
ClassLoaderclassLoader=Cmower.class.getClassLoader();
Pathpath=Paths.get(classLoader.getResource(“cmower.txt”).getPath());
try(FileChannelfileChannel=FileChannel.open(path)){
MappedByteBuffermappedByteBuffer=fileChannel.map(MapMode.READ_ONLY,0,fileChannel.size());
if(mappedByteBuffer!=null){
charBuffer=Charset.forName(“UTF-8”).decode(mappedByteBuffer);
}
System.out.println(charBuffer.toString());
}catch(IOExceptione){
e.printStackTrace();
}
由于decode()办法的参数是MappedByteBuffer,这就意味着我们是从内存中而不是磁盘中读入的文件内容,所以速度会十分快。
02、运用MappedByteBuffer写入文件
假定如今要把下面的内容写入到一个文件,名叫cmower1.txt。
缄默王二,《Web全栈开发进阶之路》作者
这个文件还没有创立,方案放在项目的classpath目录下。
Pathpath=Paths.get(“cmower1.txt”);
详细位置见下图所示。
然后,创立文件的通道。
FileChannelfileChannel=FileChannel.open(path,StandardOpenOption.READ,StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING)
依然运用的open办法,不过增加了3个参数,前2个很好了解,表示文件可读(READ)、可写(WRITE);第3个参数TRUNCATE_EXISTING的意义是假如文件曾经存在,并且文件曾经翻开将要停止WRITE操作,则其长度被截断为0。
紧接着,依然调用FileChannel类的map办法从channel中获取MappedByteBuffer。
MappedByteBuffermappedByteBuffer=fileChannel.map(MapMode.READ_WRITE,0,1024);
这一次,我们把形式调整为MapMode.READ_WRITE,并且指定文件大小为1024,即1KB的大小。然后运用MappedByteBuffer中的put()办法将CharBuffer的内容保管到文件中。详细的代码示例如下。
CharBuffercharBuffer=CharBuffer.wrap(“缄默王二,《Web全栈开发进阶之路》作者”);
Pathpath=Paths.get(“cmower1.txt”);
try(FileChannelfileChannel=FileChannel.open(path,StandardOpenOption.READ,StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING)){
MappedByteBuffermappedByteBuffer=fileChannel.map(MapMode.READ_WRITE,0,1024);
if(mappedByteBuffer!=null){
mappedByteBuffer.put(Charset.forName(“UTF-8”).encode(charBuffer));
}
}catch(IOExceptione){
e.printStackTrace();
}
能够翻开cmower1.txt查看一下内容,确认预期的内容有没有写入胜利。
03、MappedByteBuffer的遗憾
听说,在Java中运用MappedByteBuffer是一件十分费事并且痛苦的事,主要表现有:
1)一次map的大小最好限制在1.5G左右,反复map会增加虚拟内存回收和重新分配的压力。也就是说,假如文件大小不肯定的话,就不太友好。
2)虚拟内存由操作系统来决议什么时分刷新到磁盘,这个时间不太容易被程序控制。
3)MappedByteBuffer的回收方式比拟诡异。
再次强调,这三种说法都是听说,我暂时才能有限,也不能肯定这种说法的精确性,很遗憾。
04、比拟文件操作的处置时间
嗨,朋友,阅读完以上的内容之后,我想你一定对内存映射文件有了大致的理解。但我置信,假如你是一名担任任的程序员,你一定还想晓得:内存映射文件的读取速度终究有多快。
为了得出结论,我叫了另外三名竞赛的选手:InputStream(普通输入流)、BufferedInputStream(带缓冲的输入流)、RandomAccessFile(随机访问文件)。
读取的对象是加勒比海盗4惊涛怪浪.mkv,大小为1.71G。
1)普通输入流
publicstaticvoidinputStream(Pathfilename){
try(InputStreamis=Files.newInputStream(filename)){
intc;
while((c=is.read())!=-1){
}
}catch(IOExceptione){
e.printStackTrace();
}
}
2)带缓冲的输入流
publicstaticvoidbufferedInputStream(Pathfilename){
try(InputStreamis=newBufferedInputStream(Files.newInputStream(filename))){
intc;
while((c=is.read())!=-1){
}
}catch(IOExceptione){
e.printStackTrace();
}
}
3)随机访问文件
publicstaticvoidrandomAccessFile(Pathfilename){
try(RandomAccessFilerandomAccessFile=newRandomAccessFile(filename.toFile(),”r”)){
for(longi=0;i<randomAccessFile.length();i++){
randomAccessFile.seek(i);
}
}catch(IOExceptione){
e.printStackTrace();
}
}
4)内存映射文件
publicstaticvoidmappedFile(Pathfilename){
try(FileChannelfileChannel=FileChannel.open(filename)){
longsize=fileChannel.size();
MappedByteBuffermappedByteBuffer=fileChannel.map(MapMode.READ_ONLY,0,size);
for(inti=0;i<size;i++){
mappedByteBuffer.get(i);
}
}catch(IOExceptione){
e.printStackTrace();
}
}
测试程序也很简单,大致如下:
longstart=System.currentTimeMillis();
bufferedInputStream(Paths.get(“jialebi.mkv”));
longend=System.currentTimeMillis();
System.out.println(end-start);
四名选手的结果如下表所示。
办法时间
普通输入流龟速,没有耐烦等出结果
随机访问文件龟速,没有耐烦等下去
带缓冲的输入流29966
内存映射文件914
普通输入流和随机访问文件都慢得要命,真的是龟速,我没有耐烦等候出结果;带缓冲的输入流的表现还不错,但相比内存映射文件就逊色多了。由此得出的结论就是:内存映射文件,上G大文件轻松处置。
05、最后
本篇文章主要引见了Java的内存映射文件,MappedByteBuffer是其灵魂,读取速度快如火箭。另外,一切这些示例和代码片段都能够在GitHub上找到——这是一个Maven项目,所以它很容易导入和运转。

未经允许不得转载:IT技术网站 » Java内存映射,上G大文件轻松处置
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载