堆外記憶體(直接記憶體)
HeapByteBuffer與DirectByteBuffer,在原理上,前者可以看出分配的buffer是在heap區域的,其實真正flush到遠端的時候會先拷貝得到直接記憶體,再做下一步操作(考慮細節還會到OS級別的核心區直接記憶體),其實發送靜態檔案最快速的方法是通過OS級別的send_file,只會經過OS一個核心拷貝,而不會來回拷貝;在NIO的框架下,很多框架會採用DirectByteBuffer來操作,這樣分配的記憶體不再是在java heap上,而是在C heap上,經過效能測試,可以得到非常快速的網路互動,在大量的網路互動下,一般速度會比HeapByteBuffer要快速好幾倍。
直接記憶體(Direct Memory)並不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域,但是這部分記憶體也被頻繁地使用,而且也可能導致OutOfMemoryError 異常出現,所以我們放到這裡一起講解。
在JDK 1.4 中新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O 方式,它可以使用Native 函式庫直接分配堆外記憶體,然後通過一個儲存在Java 堆裡面的DirectByteBuffer 物件作為這塊記憶體的引用進行操作。這樣能在一些場景中顯著提高效能,因為避免了在Java 堆和Native 堆中來回複製資料。
import sun.nio.ch.DirectBuffer;
import java.nio.ByteBuffer;
public class Main {
public static void main(String[] args) throws InterruptedException {
System.out.println("Hello World!");
ByteBuffer bb = ByteBuffer.allocateDirect(1024 * 1024 * 128);
Thread.sleep(10000);
((DirectBuffer)bb).cleaner().clean();
Thread.sleep(10000 );
}
}
可以在工作管理員那觀察變化
堆外記憶體的優點和缺點
堆外記憶體,其實就是不受JVM控制的記憶體。相比於堆內記憶體有幾個優勢:
1 減少了垃圾回收的工作,因為垃圾回收會暫停其他的工作(可能使用多執行緒或者時間片的方式,根本感覺不到)
2 加快了複製的速度。因為堆內在flush到遠端時,會先複製到直接記憶體(非堆記憶體),然後在傳送;而堆外記憶體相當於省略掉了這個工作。
而福之禍所依,自然也有不好的一面:
1 堆外記憶體難以控制,如果記憶體洩漏,那麼很難排查
2 堆外記憶體相對來說,不適合儲存很複雜的物件。一般簡單的物件或者扁平化的比較適合。