大多数操作系统都支持利用虚拟内存实现来将一个文件或者一个文件的一部分“映射”到内存中。
在java中这种操作相当简单,相对带缓冲区的的顺序输入流要稍快一点,而比RandomAccessFile要快很多。具体做法如下。
首先,从文件中获得一个通道,通道适用于磁盘文件的一种抽象,它使得我们可以访问诸如内存映射、文件加锁机制以及文件件快速数据传输等操作系统系统特征。我们可以通过调用getChannel方法来获得通道。
FileInputStream inputStream = new FileInputStream(fileName);
FileChannel channel = inputStream.getChannel();
然后,通过调用FileChannel类的map方法从这个通道中获得一个MappedByteBuffer,可以指定你想要映射的文件区域与映射模式,有三种模式可以 选择:
FileChannel.MapMode.READ_ONLY : 所产生的缓冲区是可读的
FileChannel.MapMode.READ_WRITE : 所产生的缓冲区可读可写
FileChannel.MapMode.PRIVATE: 所产生的缓冲区是可写的,但是任读写改都不会对源文件造成更改
这里我就写个程序来看看FileInputStream、BufferedInputStrream、RandomAccessFile、MappedFile速度差异
首先用到了CRC32这个类,在java.util.zip包下,该类用于计算文件的32为的循环冗余校验和,这个数值经常用来判断一个文件是否已经损坏。
用法如下:
CRC32 crc = new CRC32();
while(hashMoreBytes){
crc.update(nextByte);
}
long result = crc.getValue();
一般大小的文件没必要用到内存映射文件,我选用的是jre下的rt.jar,大约49MB。
好了,我的源程序如下:
public class MemeryMappedFile {
public static long checkSumMappedFile(String fileName)throws IOException{
FileInputStream fis = new FileInputStream(fileName);
FileChannel channel = fis.getChannel();
CRC32 crc = new CRC32();
int length = (int) channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
for(int i=0; i<length; i++){
int c = buffer.get(i);
crc.update(c);
}
return crc.getValue();
}
public static long checkSumInputStream(String fileName)throws IOException{
InputStream inputStream = new FileInputStream(fileName);
CRC32 crc = new CRC32();
int c;
while((c = inputStream.read()) != -1){
crc.update(c);
}
return crc.getValue();
}
public static long checkSumBufferedInputStream(String fileName)throws IOException{
InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
CRC32 crc = new CRC32();
int c;
while((c = inputStream.read()) != -1){
crc.update(c);
}
return crc.getValue();
}
public static long checkSumRandomAccessFile(String fileName)throws IOException{
RandomAccessFile randomFile = new RandomAccessFile(fileName,"r");
long length = randomFile.length();
CRC32 crc = new CRC32();
for(long i=0; i<length; i++){
randomFile.seek(i);
int c = randomFile.readByte();
crc.update(c);
}
return crc.getValue();
}
public static void main(String [] args)throws Exception{
String fileName = "C:\\Program Files\\Java\\jre7\\lib\\rt.jar";
long start,end;
long value;
start = System.currentTimeMillis();
value = checkSumMappedFile(fileName);
end = System.currentTimeMillis();
System.out.println("内存映射文件 CRC32 校验结果 :" + value);
System.out.println("内存映射文件 耗时 : " + (end - start) + "毫秒");
start = System.currentTimeMillis();
value = checkSumInputStream(fileName);
end = System.currentTimeMillis();
System.out.println("InputStream CRC32 校验结果 :" + value);
System.out.println("Inputstream 耗时 : " + (end - start) + "毫秒");
start = System.currentTimeMillis();
value = checkSumBufferedInputStream(fileName);
end = System.currentTimeMillis();
System.out.println("BufferedInputStream CRC32 校验结果 :" + value);
System.out.println("BufferedInputStream 耗时 : " + (end - start) + "毫秒");
start = System.currentTimeMillis();
value = checkSumRandomAccessFile(fileName);
end = System.currentTimeMillis();
System.out.println("RandomAccessFile CRC32 校验结果 :" + value);
System.out.println("RandomAccessFile 耗时 : " + (end - start) + "毫秒");
}
}
在我的电脑上运行的结果为:
内存映射文件 CRC32 校验结果 :1450405243
内存映射文件 耗时 : 1516毫秒
InputStream CRC32 校验结果 :1450405243
Inputstream 耗时 : 52781毫秒
BufferedInputStream CRC32 校验结果 :1450405243
BufferedInputStream 耗时 : 1672毫秒
RandomAccessFile CRC32 校验结果 :1450405243
RandomAccessFile 耗时 : 79047毫秒
-----------------------------------------------------------------------------
方 法 耗 时(毫秒)
-----------------------------------------------------------------------------
普通输入流 52781
带缓冲的输入流 1672
随机访问文件 79047
内存映射文件 1516
------------------------------------------------------------------------------
效果还是很明显的: 内存映射文件最快,比带缓冲区的输入流稍快,但是比普通的输入流和随机访问文件快很多的,尤其是比随机访问文件快了50多倍。