Java中的Heap Buffer與Direct Buffer

在使用Java NIO時,會經常和ByteBuffer打交道(吐槽下,每次手動flip切換讀寫模式太不友好)。在空Buffer創建時,有兩種方式:

ByteBuffer.allocateDirect(capacity)
ByteBuffer.allocate(capacity)

那麼這兩種Buffer的分配又有什麼不一樣呢?

Heap Buffer

字面意思,在java heap上分配的內存。此塊內存區域受JVM管理,GC負責回收。使用時無需擔心Heap Buffer的回收問題。

Direct Buffer

堆外內存(說非堆不太準確,畢竟非堆區域不止這一塊),時分配在C Heap上的Buffer,由於不屬於JVM HEAP,管理/監控起來會比較困難,但也會被GC回收。DirectByteBuffer 自身是(Java)堆內的,它背後真正承載數據的buffer是在(Java)堆外——native memory中的。這是 malloc() 分配出來的內存,是用戶態的。

那麼爲什麼有了Heap Buffer還需要Direct Buffer呢?

在JVM的垃圾回收器裏,除了CMS,都是需要移動對象的;如果要把一個Java裏的 byte[] 對象的引用傳給native代碼,讓native代碼直接訪問數組的內容的話,就必須要保證native代碼在訪問的時候這個 byte[] 對象不能被移動,也就是要被“pin”(釘)住。

於是就出現了Direct Buffer,Direct Buffer是在C Heap中分配的內存,不像JVM堆內存是邏輯的,雖然也會被GC管理,但他是通過PhantomReference來達到的,正常的young gc或者mark and compact的時候不會在內存裏移動。例如使用在傳輸數據時(磁盤IO傳輸和Socket傳輸都屬於fd),如果傳入HeapByteBuffer,首先會把HeapByteBuffer 背後的 byte[] 的內容拷貝到一個 DirectByteBuffer,然後再發送DirectByteBuffer中的數據。如果直接使用DirectByteBuffer的話,就會少了一次HeapByteBuffer->DirectByteBuffer的拷貝。

但是使用DirectByteBuffer也是有代價的,DirectByteBuffer比HeapByteBuffer的創建開銷更大,所以如果要使用DirectByteBuffer的話最好還是複用,避免過多的創建。

堆外內存的監控

堆外內存不像堆內內存監控那麼簡單,不能直接看堆信息,但可以通過一些監控工具來查看

jconsole

JCONSOLE監控堆外

jvisualvm

jvisualvm
注:需要安裝VisualVM-BufferMonitor和VisualVM-MBeans插件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章