内存映射文件
1. 前言
在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度。
- 内存映射文件允许Java程序直接从内存中读取文件内容,通过将整个或部分文件映射到内存,由操作系统来处理加载请求和写入文件,应用只需要和内存打交道,这使得IO操作非常快。加载内存映射文件所使用的内存在Java堆区之外。Java编程语言支持内存映射文件,通过java.nio包和MappedByteBuffer 可以从内存直接读写文件。
- 内存映射IO最大的优点可能在于性能,这对于建立高频电子交易系统尤其重要。内存映射文件通常比标准通过正常IO访问文件要快。另一个巨大的优势是内存映 射IO允许加载不能直接访问的潜在巨大文件 。经验表明,内存映射IO在大文件处理方面性能更加优异。尽管它也有不足——增加了页面错误的数目。由于操作系统只将一部分文件加载到内存,如果一个请求 页面没有在内存中,它将导致页面错误。同样它可以被用来在两个进程中共享数据。
- 大多数主流操作系统比如Windows平台,UNIX,Solaris和其他类UNIX操作系统都支持内存映射IO和64位架构,几乎可以将所有文件映射到内存并通过JAVA编程语言直接访问。
2. 四种速度测试方案:
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) {
//通过FileInputStream流读取
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();
}
//通过用BufferedInputStream包装的FileInputStream流读取
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();
}
}
}
3. 内存映射使用步骤:
3.1. 1、建立通道:
3.1.1.1.1. 通过FileChannel的静态方法:
static FileChannel open(Path path, OpenOption... options)
3.1.1.1.2. 通过FileChannel的静态方法:
static FileChannel open(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
3.1.1.1.3. FileInputStream类的方法:
FileChannel getChannel()
3.2. 2、从通道将文件映射到内存缓冲区:
FileChannel类的方法:
abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)
FileChannel.MapMode是一个内部静态类。
类型 | 字段 | 说明 |
---|---|---|
static FileChannel.MapMode | PRIVATE | Mode for a private (copy-on-write) mapping. |
static FileChannel.MapMode | READ_ONLY | Mode for a read-only mapping. |
static FileChannel.MapMode | READ_WRITE | Mode for a read/write mapping. |
3.3. 3、从缓冲区读取数据
使用ByteBuffer的方法:
- get() getChar()读取; 用hasremaining()判断结尾等。