1. 程式人生 > >堆外記憶體(直接記憶體)

堆外記憶體(直接記憶體)

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 堆外記憶體相對來說,不適合儲存很複雜的物件。一般簡單的物件或者扁平化的比較適合。