先解釋一下二者的區別: java
Non-direct ByteBuffer內存是分配在堆上的,直接由Java虛擬機負責垃圾收集,你能夠把它想象成一個字節數組的包裝類,以下僞碼所示: 數組
HeapByteBuffer extends ByteBuffer {
byte[] content;
int position, limit, capacity;
......
} 緩存
而Direct ByteBuffer是經過JNI在Java虛擬機外的內存中分配了一塊(因此即便在運行時經過-Xmx指定了Java虛擬機的最大堆內存,仍是可能實例化超出該大小的DirectByteBuffer),該內存塊並不直接由Java虛擬機負責垃圾收集,可是在Direct ByteBuffer包裝類被回收時,會經過Java Reference機制來釋放該內存塊。以下僞碼所示: 網絡
DirectByteBuffer extends ByteBuffer {
long address;
int position, limit, capacity;
protected void finalize() throws Throwable{
//釋放內存塊,該段代碼僅僅用於演示,真正的Direct ByteBuffer並非經過finalize來釋放的
releaseAddress();
......
}
......
} 併發
我相信大部分朋友們對上面的區別都應該很瞭解,那麼還有什麼其餘的區別呢?嘿嘿,讓咱們稍微深刻一點,翻到sun.nio.ch.IOUtil.java,絕大部分Channel類都是經過這個工具類和外界進行通信的,如FileChannel/SocketChannel等等。我簡單的用僞碼把write方法給表達出來(read方法也差很少,就很少作說明了) 框架
int write(ByteBuffer src, ......) {
if (src instanceof DirectBuffer)
return writeFromNativeBuffer(...);
ByteBuffer direct = getTemporaryDirectBuffer(src);
writeFromNativeBuffer(direct,......);
updatePosition(src);
releaseTemporaryDirectBuffer(direct);
} 工具
是的,在發送和接收前會把Non-direct ByteBuffer轉換爲Direct ByteBuffer,而後再進行相關的操做,最後更新原始ByteBuffer的position。這意味着什麼?假設咱們要從網絡中讀入一段數據,再把這段數據發送出去的話,採用Non-direct ByteBuffer的流程是這樣的: 性能
網絡 --> 臨時的Direct ByteBuffer --> 應用 Non-direct ByteBuffer --> 臨時的Direct ByteBuffer --> 網絡 spa
而採用Direct ByteBuffer的流程是這樣的: 設計
網絡 --> 應用 Direct ByteBuffer --> 網絡
能夠看到,除開構造和析構臨時Direct ByteBuffer的時間外,起碼還能節約兩次內存拷貝的時間。那麼是否在任何狀況下都採用Direct Buffer呢?
不是。對於大部分應用而言,兩次內存拷貝的時間幾乎能夠忽略不計,而構造和析構Direct Buffer的時間卻相對較長。在JVM的實現當中,某些方法會緩存一部分臨時DirectByteBuffer,意味着若是採用Direct ByteBuffer僅僅能節約掉兩次內存拷貝的時間,而沒法節約構造和析構的時間。就用Sun的實現來講,write(ByteBuffer)和read(ByteBuffer)方法都會緩存臨時Direct ByteBuffer,而write(ByteBuffer[])和read(ByteBuffer[])每次都生成新的臨時Direct ByteBuffer。
根據這些區別,我會提出以下的建議:
· 若是你作中小規模的應用(在這裏,應用大小是按照使用ByteBuffer的次數和規模來作劃分的),並且並不在意這該死的細節問題,請選擇Non-direct ByteBuffer · 若是採用Direct ByteBuffer後性能並無出現你所期待的變化,請選擇Non-direct ByteBuffer · 若是沒有Direct ByteBuffer Pool,儘可能不要使用Direct ByteBuffer · 除非你肯定該ByteBuffer會長時間存在,而且和外界有頻繁交互,可採用Direct ByteBuffer · 若是採用Non-direct ByteBuffer,那麼採用非彙集(gather)的write/read(ByteBuffer)效果反而可能超出彙集的write/read(ByteBuffer[]),由於彙集的write/read的臨時Direct ByteBuffer是非緩存的基本上,採用Non-direct ByteBuffer老是對的!由於內存拷貝須要的開銷對大部分應用而言均可以忽略不計。不過我作的是大規模的網絡併發框架,所以對這些細節問題仍是有必要有深刻認識的,而且根據這些細節來調節本身的Buffer繼承體系(再次抱怨,ByteBuffer沒法擴展實在是一個很是很是很是費解的設計)
注:前面提到的「即便在運行時經過-Xmx指定了Java虛擬機的最大堆內存,仍是可能實例化超出該大小的Direct ByteBuffer」中的多是指能夠經過-XX:MaxDirectMemorySize=<size>來指定Direct ByteBuffer實例最多可使用的內存總數。如指定-XX:MaxDirectMemorySize=1024,則系統中全部存活的Direct ByteBuffer總內存數不能超過1024字節。