1、什麼是內存映射文件java
內存映射文件,是由一個文件到一塊內存的映射,能夠理解爲將一個文件映射到進程地址,而後能夠經過操做內存來訪問文件數據。說白了就是使用虛擬內存將磁盤的文件數據加載到虛擬內存的內存頁,而後就能夠直接操做內存頁數據。緩存
咱們讀寫一個文件使用read()和write()方法,這兩個方法是調用系統底層接口來傳輸數據,由於內核空間的文件頁和用戶空間的緩衝區沒有一一對應,因此讀寫數據時會在內核空間和用戶空間之間進行數據拷貝,在操做大量文件數據時會致使性能很低,使用內存映射文件能夠很是高效的操做大量文件數據。app
經過內存映射機制操做文件比使用常規方法和使用FileChannel讀寫高效的多。dom
內存映射文件使用文件系統創建從用戶空間到可用文件系統頁的虛擬內存映射,這樣作有如下好處:性能
1.用戶進程把文件數據當內存數據,無需調用read()或write()大數據
2.當用戶進程接觸到映射內存空間,會自動產生頁錯誤,從而將文件數據從磁盤讀到內存;若用戶空間進程修改了內存頁數據,相關頁會自動標記並刷新到磁盤,文件被更新操作系統
3.操做系統的虛擬內存對內存頁進行高速緩存,自動根據系統負載進行內存管理code
4.用戶空間和內核空間的數據老是一一對應,無需執行緩衝區拷貝接口
5.大數據的文件使用映射,無需消耗大量內存便可進行數據拷貝進程
2、如何建立內存映射文件
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); FileChannel fc = raf.getChannel(); //將test.txt文件全部數據映射到虛擬內存,並只讀 MappedByteBuffer mbuff = fc.map(MapMode.READ_ONLY, 0, fc.size());
映射文件的範圍不該超過文件的實際大小,不然文件的大小會被增大到指定的大小
//實際文件只有10個字節,執行下面代碼後,文件內容變爲10000個字節 MappedByteBuffer mbuff = fc.map(MapMode.READ_WRITE, 0, 10000);
第一個參數MapMode是個三個值:
1. MapMode.READ_ONLY:只讀,若FileChannel不可讀,拋出NonReadableChannelException
2. MapMode.READ_WRITE:可讀寫,若FileChannel不可寫,拋出NonWritableChannelException
3. MapMode.PRIVATE:建立一個寫時拷貝的映射,修改映射內存頁的數據只對MappedByteBuffer可視
3、MappedByteBuffer API
MappedByteBuffer是ByteBuffer的子類,因此可被通道讀寫。MappedByteBuffer提供的方法:
1. load():加載整個文件到內存
2. isLoaded():判斷文件數據是否所有加載到了內存
3. force():將緩衝區的更改刷新到磁盤
4、通道到通道傳輸
將文件數據從一個通道傳輸到另外一個通道,FileChannel提供下面2個高效方法:
1. transferTo(long position, long count, WritableByteChannel target):傳輸到哪一個可寫通道
2. transferFrom(ReadableByteChannel src, long position, long count):從哪一個可讀通道傳輸過來
通道到通道傳輸數據使用上面2個方法不須要使用中間緩衝區。只有FileChannel有以上方法,因此Channel-to-Channel中必須有一個是FileChannel。
對於傳輸大量數據,以上2個方法的效率是很是高的。
//獲取test0.txt文件的通道句柄 RandomAccessFile raf0 = new RandomAccessFile("test0.txt", "r"); FileChannel fc0 = raf0.getChannel(); //獲取test1.txt文件的通道句柄 RandomAccessFile raf1 = new RandomAccessFile("test1.txt", "rw"); FileChannel fc1 = raf1.getChannel(); //將test0.txt傳輸到test1.txt fc0.transferTo(0, fc0.size(), fc1); //強制刷新數據到磁盤 fc0.force(true); //關閉通道 raf1.close(); raf0.close();