Java NIO以內存映射文件——MappedByteBuffer

大多數操做系統均可以利用虛擬內存實現將一個文件或者文件的一部分"映射"到內存中。而後,這個文件就能夠看成是內存數組來訪問,這比傳統的文件要快得多。java

內存映射文件的一個關鍵優點是操做系統負責真正的讀寫,即便你的程序在剛剛寫入內存後就掛了,操做系統仍然會將內存中的數據寫入文件系統。另一個更突出的優點是共享內存,內存映射文件能夠被多個進程同時訪問,起到一種低時延共享內存的做用。數組

那麼,如何將一個文件映射到內存呢?app

  1. 從文件中得到一個通道(channel)
FileChannel channel = FileChannel.open(path,options);

這裏options指定映射模式,支持的模式有三種:dom

  • FileChannel.MapMode.READ_ONLY:所產生的緩衝區是隻讀的。
  • FileChannel.MapMode.READ_WRITE:所產生的緩衝區是可寫的,任何修改都會在某個時刻寫回到文件中。
    注意,其餘映射同一個文件的程序可能不能當即看到這些修改,多個程序同時進行文件映射的確切行爲是依賴
    於操做系統的。
  • FileChannel.MapMode.PRIVATE:所產生的緩衝區是可寫的,可是任何修改對該緩衝區來講都是私有的,不
    會傳播到文件中。
  1. 調用FileChannel的map方法
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY,0,length);

接下來經過計算一個40MB文件的CRC32校驗和來比較傳統的文件輸入和內存映射文件的速度。操作系統

傳統的文件輸入包括:code

  • 普通輸入流(InputStream)
  • 帶緩衝的輸入流(BufferedInputStream)
  • 隨機訪問文件(RandomAccessFile)

程序以下:進程

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.CRC32;

/**
 * Created by lbd on 2017/1/11.
 */
public class MemoryMapTest {
    public static long checksumInputStream(Path filename) throws IOException {  //普通輸入流
        try (InputStream in = Files.newInputStream(filename)) {
            CRC32 crc = new CRC32();
            int c;
            while ((c = in.read()) != -1)
                crc.update(c);
            return crc.getValue();
        }
    }

    public static long checksumBufferedInputStream(Path filename) throws IOException {  //帶緩衝的輸入流
        try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(filename))){
            CRC32 crc = new CRC32();
            int c;
            while ((c = in.read()) != -1)
                crc.update(c);
            return crc.getValue();
        }
    }

    public static long checksumRandomAccessFile(Path filename) throws IOException {  //隨機訪問文件
        try (RandomAccessFile file = new RandomAccessFile(filename.toFile(),"r")){
            CRC32 crc = new CRC32();
            long length = file.length();

            for (long p = 0; p < length; p++){
                file.seek(p);
                int c = file.readByte();
                crc.update(c);
            }
            return crc.getValue();
         }
    }

    public static long checksumMappedFile(Path filename) throws IOException {  //內存映射文件
        try (FileChannel channel = FileChannel.open(filename)){
            CRC32 crc = new CRC32();
            int length = (int)channel.size();
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY,0,length);

            for (int p = 0; p < length; p++){
                int c = buffer.get(p);
                crc.update(c);
            }
            return crc.getValue();
        }
    }

    public static void main(String[] args) throws IOException {
        System.out.println("Input Stream:");
        long start = System.currentTimeMillis();
        Path filename = Paths.get(args[0]);
        long crcValue = checksumInputStream(filename);
        long end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");
        System.out.println();

        System.out.println("Buffered Input Stream:");
        start = System.currentTimeMillis();
        crcValue = checksumBufferedInputStream(filename);
        end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");
        System.out.println();

        System.out.println("Random Access File:");
        start = System.currentTimeMillis();
        crcValue = checksumRandomAccessFile(filename);
        end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");
        System.out.println();

        System.out.println("Mapped File:");
        start = System.currentTimeMillis();
        crcValue = checksumMappedFile(filename);
        end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");
    }
}

輸出結果以下:ip

Input Stream:
c644b1f1
42317 milliseconds

Buffered Input Stream:
c644b1f1
329 milliseconds

Random Access File:
c644b1f1
57781 milliseconds

Mapped File:
c644b1f1
207 milliseconds

能夠明顯看出,內存映射文件速度比普通輸入流和隨機訪問文件快得多,比帶緩衝的輸入流稍微快一些。內存

相關文章
相關標籤/搜索