Thinking in java(内存映射文件)

在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如下为一个对比实验。

package test;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class Test {

	public static void main(String[] args) {
		try {
			FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");
			int sum=0;
			int n;
			long t1=System.currentTimeMillis();
			try {
				while((n=fis.read())>=0){
					sum+=n;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			long t=System.currentTimeMillis()-t1;
			System.out.println("sum:"+sum+"  time:"+t);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");
			BufferedInputStream bis=new BufferedInputStream(fis);
			int sum=0;
			int n;
			long t1=System.currentTimeMillis();
			try {
				while((n=bis.read())>=0){
					sum+=n;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			long t=System.currentTimeMillis()-t1;
			System.out.println("sum:"+sum+"  time:"+t);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		MappedByteBuffer buffer=null;
		try {
			buffer=new RandomAccessFile("/home/tobacco/test/res.txt","rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1253244);
			int sum=0;
			int n;
			long t1=System.currentTimeMillis();
			for(int i=0;i<1253244;i++){
				n=0x000000ff&buffer.get(i);
				sum+=n;
			}
			long t=System.currentTimeMillis()-t1;
			System.out.println("sum:"+sum+"  time:"+t);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

测试文件为一个大小为1253244字节的文件。测试结果:

sum:220152087  time:1464
sum:220152087  time:72
sum:220152087  time:25

说明读数据无误。删去其中的数据处理部分。

package test;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class Test {

	public static void main(String[] args) {
		try {
			FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");
			int sum=0;
			int n;
			long t1=System.currentTimeMillis();
			try {
				while((n=fis.read())>=0){
					//sum+=n;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			long t=System.currentTimeMillis()-t1;
			System.out.println("sum:"+sum+"  time:"+t);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			FileInputStream fis=new FileInputStream("/home/tobacco/test/res.txt");
			BufferedInputStream bis=new BufferedInputStream(fis);
			int sum=0;
			int n;
			long t1=System.currentTimeMillis();
			try {
				while((n=bis.read())>=0){
					//sum+=n;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			long t=System.currentTimeMillis()-t1;
			System.out.println("sum:"+sum+"  time:"+t);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		MappedByteBuffer buffer=null;
		try {
			buffer=new RandomAccessFile("/home/tobacco/test/res.txt","rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1253244);
			int sum=0;
			int n;
			long t1=System.currentTimeMillis();
			for(int i=0;i<1253244;i++){
				//n=0x000000ff&buffer.get(i);
				//sum+=n;
			}
			long t=System.currentTimeMillis()-t1;
			System.out.println("sum:"+sum+"  time:"+t);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

测试结果:

sum:0  time:1458
sum:0  time:67
sum:0  time:8

由此可见,将文件部分或者全部映射到内存后进行读写,速度将提高很多。

这是因为内存映射文件首先将外存上的文件映射到内存中的一块连续区域,被当成一个字节数组进行处理,读写操作直接对内存进行操作,而后再将内存区域重新映射到外存文件,这就节省了中间频繁的对外存进行读写的时间,大大降低了读写时间。

Ps :该文章只是在本人学习中检索到的相对而言比较专业的文章,但是没有经过实践的验证。

时间: 2024-10-12 08:08:49

Thinking in java(内存映射文件)的相关文章

java 内存映射文件

内存映射文件提供了Java有可能达到的最快IO操作,故高性能Java应用应使用“内存映射文件”来持久化数据,尤其是对于较大文件来说它确实比I/O流要快很多,但是小文件却未必. 内存映射文件可以加载大文件,文件的size上限取决于操作系统(内存的可寻址范围):32位,不能超过4GB,即2^32比特:64位,你几乎可以将任何文件映射到内存中并直接使用Java访问. 内存映射文件允许你使用direct或者non-direct 字节缓存(Byte buffer)来直接读写内存. 内存映射文件的一个关键优

Java 内存映射文件MappedByteBuffer

MappedByteBuffer是java nio引入的文件内存映射方案,读写性能极高.在NIO中主要用到普通的输入流,带缓冲的输入流,RandomAccessFile和MappedByteBuffer. 现在我们来看看这四种流的效率,废话少说直接上代码. 我们采用CRC32来循环冗余校验.CRC32在java.util.zip包中. 1.普通的输入流. public static long checksumInputStream(Path filename) throws IOExceptio

Java内存分配(直接内存、堆内存、Unsafel类、内存映射文件)

1.Java直接内存与堆内存-MarchOn 2.Java内存映射文件-MarchOn 3.Java Unsafe的使用-MarchOn 简单总结: 1.内存映射文件 读文件时候一般要两次复制:从磁盘复制到内核空间再复制到用户空间,内存映射文件避免了第二次复制,且内存分配在内核空间,应用程序访问的就是操作系统的内核内存空间,因此极大提高了读取效率.写文件同理. 2.堆内存分配与直接内存分配: Java申请空间时通常是从JVM堆内存分配的,即 ByteBuffer.allocate(int cap

Java利用内存映射文件实现按行读取文件

我们知道内存映射文件读取是各种读取方式中速度最快的,但是内存映射文件读取的API里没有提供按行读取的方法,需要自己实现.下面就是我利用内存映射文件实现按行读取文件的方法,如有错误之处请指出,或者有更好更快的实现方式麻烦也提供一下代码. 代码如下: public class testMemoryMappedFile { public static void main(String[] agrs) throws IOException{ RandomAccessFile memoryMappedFi

《Java核心技术卷二》笔记(二)文件操作和内存映射文件

文件操作 上一篇已经总结了流操作,其中也包括文件的读写.文件系统除了读写以为还有很多其他的操作,如复制.移动.删除.目录浏览.属性读写等.在Java7之前,一直使用File类用于文件的操作.Java7提供了Path,Paths,Files,FileSystem等类,使文件操作变得简单和全面.此外还有很多第三方库也提供了文件操作的便捷类如common.io中的FileUtils类,Ant api提供的FileSet等类. 1.File类的使用 Java7之前版本中,File类即代表了路径对象也封装

JAVA NIO之浅谈内存映射文件原理与DirectMemory

Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原理. 在传统的文件IO操作中,我们都是调用操作系统提供的底层标准IO系统调用函数  read().write() ,此时调用此函数的进程(在JAVA中即java进程)由当前的用户态切换到内核态,然后OS的内核代码负责将相应的文件数据读取到内核的IO缓冲区,然后再把数据从内核IO缓冲区拷贝到进程的私有

Java NIO 应用 -- 使用内存映射文件实现进程间通信

一看到 Java NIO 的内存映射文件(MappedByteBuffer),让我立即就联想到 Windows 系统的内存映射文件.Windows 系统的内存映射文件能用来在多个进程间共享数据,即进程间的共享内存,是通过把同一块内存区域映射到不同进程的地址空间中,从而达到共享内存. Java NIO 的内存映射文件和 Windows 系统下的一样,都能把物理文件的内容映射到内存中,那么 MappedByteBuffer 是否能用来在不同 Java 进程(JVM) 间共享数据呢?答案是肯定的,这样

【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射

内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件.有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问.这种解决办法能大大简化修改文件的代码.fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中.注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大:也就是说,它还可以映射一个大文件的某个小片断. MappedByteB

JAVA I/O(三)内存映射文件

<Java编程思想>中对内存映射文件有详细的介绍,此处仅做简单记录和总结.内存映射文件允许创建和修改因为太大而不能放入内存的文件. 1. 内存映射文件简单实例 import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class LargeMappedFiles { priv