NIO 之 MappedByteBuffer

可參考: MappedByteBuffer以及ByteBufer的底層原理

概述

Bytebuffer分爲兩種:間接地和直接的,所謂直接就是指MappedByteBuffer,直接使用內存映射(java的話就意味着在JVM之外分配虛擬地址空間);而間接的ByteBuffer是在JVM的堆上面的。間接緩衝區就是我們通常說的堆緩衝區。
直接緩衝區 java內部是使用 DirectByteBuffer 來實現的。
堆緩衝區java內部是使用 HeapByteBuffer 來實現的。

class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {}

class HeapByteBuffer extends ByteBuffer {}

映射的字節緩衝區(MappedByteBuffer ) 不提供關閉或銷燬方法。也就是說,創建完直接緩衝區,就一直有效,直到緩衝區本身被垃圾收集。

映射字節緩衝區的內容可以在任何時間改變,例如,如果映射的文件的對應區域的內容由該程序或其他程序改變。無論這種變化是否發生,當它們發生時,都是依賴於操作系統的,因此不明確。

映射的字節緩衝區的全部或部分可能在任何時間變得不可訪問,例如映射的文件被截斷。試圖訪問映射字節緩衝區的不可訪問區域不會改變緩衝區的內容,並且會導致在訪問時或稍後某個時間拋出一個未指定的異常。因此,強烈建議採取適當的預防措施,以避免由該程序或由同時運行的程序操縱映射文件,除了讀取或寫入文件的內容。

MappedByteBuffer 類結構

public abstract class MappedByteBuffer extends ByteBuffer {
    public final MappedByteBuffer load( )
    public final boolean isLoaded( )
    public final MappedByteBuffer force( )
}

MappedByteBuffer 繼承了 ByteBuffer 。

load() 方法

load( )方法會整個文件加載到內存中。

此方法盡最大努力確保當它返回時,該緩衝區的內容駐留在物理內存中。調用此方法可能會導致一些頁面錯誤和I/O操作發生。

操作系統會採用虛擬內存映射,把緩衝區和文件建立虛擬內存映射。此映射使得操作系統的底層虛擬內存子系統可以根據需要將文件中相應區塊的數據讀進內存。已經在內存中或通過驗證的頁會佔用實際內存空間,並且在它們被讀進 RAM 時會擠出最近較少使用的其他內存頁。(swap in,swap out)

在一個映射緩衝區上調用 load( )方法會是一個代價高的操作,因爲它會導致大量的頁調入(page-in),具體數量取決於文件中被映射區域的實際大小。然而,load( )方法返回並不能保證文件就會完全加載到內存,這是由於請求頁面調入是動態的。具體結果會因某些因素而有所差異,這些因素包括:操作系統、文件系統,可用 Java 虛擬機內存,最大 Java 虛擬機內存,垃圾收集器實現過程等等。

請小心使用 load( )方法,它可能會導致您不希望出現的結果。該方法的主要作用是爲提前加載文件埋單,以便後續的訪問速度可以儘可能的快。
對於那些要求近乎實時訪問的程序,解決方案就是預加載。但是請記住,不能保證全部頁都加載到內存,不管怎樣,之後可能還會有頁調入發生(操作系統自己維護,依賴操作系統的實現)。內存頁什麼時候swap in 和 swap out 受多個因素影響,這些因素中的許多都是不受 Java 虛擬機控制的。
JDK 1.4 的 NIO 並沒有提供一個可以把頁面固定到物理內存上的API,儘管一些操作系統是支持這樣做的。對於大多數程序,特別是交互性的或其他事件驅動(event-driven)的程序而言,爲提前加載文件消耗資源是不划算的。在實際訪問時分攤頁調入開銷纔是更好的選擇。讓操作系統根據需要來調入頁意味着不訪問的頁永遠不需要被加載。同預加載整個被映射的文件相比,這很容易減少 I/O 活動總次數。操作系統已經有一個複雜的內存管理系統了,就讓它來替您完成此工作吧!

isLoaded() 方法

我們可以通過調用 isLoaded( )方法來判斷一個被映射的文件是否完全加載內存了。
如果該方法返回ture,意味着該緩衝區中的所有數據很可能完全加載到物理內存中了,因此可以在不產生任何虛擬內存頁錯誤或I/O操作的情況下訪問。
不過,該方法返回false,並不一定意味着緩衝區的內容沒有加載到物理內存中。

返回值是一個提示,而不是一個保證,因爲底層操作系統在調用該方法返回的時候可能已經分出了一些緩衝區的數據。

force() 方法

該方法會強制將此緩衝區上的任何更改寫入映射到永久磁盤存儲器上。

如果映射到該緩衝區的文件駐留在本地存儲設備上,那麼當該方法返回時,它保證對創建的緩衝區進行的所有更改,或者自上次調用該方法後,將被寫入該設備。
如果文件不駐留在本地設備上,則不提供這樣的保證。

當用 MappedByteBuffer 對象來更新一個文件,您應該總是使用 MappedByteBuffer.force( )而非 FileChannel.force( ),因爲通道對象可能
不清楚通過映射緩衝區做出的文件的全部更改。MappedByteBuffer 沒有不更新文件元數據的選項——元數據總是會同時被更新的。

如果映射是以 MapMode.READ_ONLY 或 MAP_MODE.PRIVATE 模式建立的,那麼調用 force( )
方法將不起任何作用,因爲永遠不會有更改需要應用到磁盤上(但是這樣做也是沒有害處的)。


想了解更多精彩內容請關注我的公衆號

本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8    
點擊這裏快速進入簡書
GIT地址:http://git.oschina.net/brucekankan/
點擊這裏快速進入GIT

發佈了132 篇原創文章 · 獲贊 79 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章