HeapByteBuffer 和 DirectByteBuffer 以及回收 DirectByteBuffer_GC

來源:互聯網
上載者:User

http://www.importnew.com/19191.html

byte buffer一般在網路互動過程中java使用得比較多,尤其是以NIO的架構中;

看名字就知道是以位元組碼作為緩衝的,先buffer一段,然後flush到終端。

而本文要說的一個重點就是HeapByteBuffer與DirectByteBuffer,以及如何合理使用DirectByteBuffer。

1、HeapByteBuffer與DirectByteBuffer,在原理上,前者可以看出分配的buffer是在heap地區的,其實真正flush到遠端時候會先拷貝得到直接記憶體,再做下一步操作(考慮細節還會到OS層級的核心區直接記憶體),其實發送靜態檔案最快速的方法是通過OS層級的send_file,只會經過OS一個核心拷貝,而不會來回拷貝;在NIO的架構下,很多架構會採用DirectByteBuffer來操作,這樣分配的記憶體不再是在java heap上,而是在C heap上,經過效能測試,可以得到非常快速的網路互動,在大量的網路互動下,一般速度會比HeapByteBuffer要快速好幾倍。

最基本的情況下

分配HeapByteBuffer的方法是: 1 ByteBuffer.allocate( int capacity);參數大小為位元組的數量

分配DirectByteBuffer的方法是: 1 ByteBuffer.allocateDirect( int capacity); //可以看到分配記憶體是通過unsafe.allocateMemory()來實現的,這個unsafe預設情況下java代碼是沒有能力可以調用到的,不過你可以通過反射的手段得到執行個體進而做操作,當然你需要保證的是程式的穩定性,既然叫unsafe的,就是告訴你這不是安全的,其實並不是不安全,而是交給程式員來操作,它可能會因為程式員的能力而導致不安全,而並非它本身不安全。

由於HeapByteBuffer和DirectByteBuffer類都是default類型的,所以你無法位元組訪問到,你只能通過ByteBuffer間接訪問到它,因為JVM不想讓你訪問到它,對了,JVM不想讓你訪問到它肯定就有它不可告人的秘密;後面我們來跟蹤下他的秘密吧。

2、前面說到了,這塊地區不是在java heap上,那麼這塊記憶體的大小是多少呢。預設是一般是64M,可以通過參數:-XX:MaxDirectMemorySize來控制,你夠牛的話,還可以用代碼控制,呵呵,這裡就不多說了。

3、直接記憶體好,我們為啥不都用直接記憶體。請注意,這個直接記憶體的釋放並不是由你控制的,而是由full gc來控制的,直接記憶體會自己檢測情況而調用system.gc(),但是如果參數中使用了DisableExplicitGC 那麼這是個坑了,所以啊,這玩意,設定不設定都是一個坑坑,所以java的最佳化有沒有絕對的,只有針對實際情況的,針對實際情況需要對系統做一些拆分做不同的最佳化。

4、那麼full gc不觸發,我想自己釋放這部分記憶體有方法嗎。可以的,在這裡沒有什麼是不可以的,呵呵。私人屬性我們都任意玩他,還有什麼不可以玩的;我們看看它的源碼中DirectByteBuffer發現有一個:Cleaner,貌似是用來搞資源回收的,經過查證,的確是,而且又看到這個對象是sun.misc開頭的了,此時既驚喜又鬱悶,呵呵,只要我能拿到它,我就能有希望消滅掉了;下面第五步我們來做個實驗。

5、因為我們的代碼全是私人的,所以我要訪問它不能直接存取,我需要通過反射來實現,OK,我知道要調用cleaner()方法來擷取它Cleaner對象,進而通過該對象,執行clean方法;(付:以下代碼大部分也取自網路上的一篇copy無數次的代碼,但是那個代碼是有問題的,有問題的部分,我將用紅色標識出來,如果沒有哪條代碼是無法啟動並執行) 1 2 3 4 5 6 7 8 9 10 11 import java.nio.ByteBuffer; import sun.nio.ch.DirectBuffer;   public class DirectByteBufferCleaner {            public static void clean( final ByteBuffer byteBuffer) {                if (byteBuffer.isDirect()) {                   ((DirectBuffer)byteBuffer).cleaner().clean();                }          } }

上述類你可以在任何位置建立都可以,這裡多謝一樓的回複,以前我的寫法是見到DirectByteBuffer類是Default類型的,因此這個類無法直接引用到,是通過反射去找到cleaner的執行個體,進而調用內部的clean方法,那樣做麻煩了,其實並不需要那麼麻煩,因為DirectByteBuffer implements了DirectBuffer,而DirectBuffer本身是public的,所以通過介面去調用內部的Clear對象來做clean方法。

我們下面來做測試來證明這個程式是有效地回收的:

在任意一個地方寫一段main方法來調用,我這裡就直接寫在這個類裡面了: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void sleep( long i) {      try {            Thread.sleep(i);       } catch (Exception e) {            /*skip*/       } } public static void main(String []args) throws Exception {         ByteBuffer buffer = ByteBuffer.allocateDirect( 1024 * 1024 * 100 );         System.out.println( "start" );         sleep( 10000 );         clean(buffer);         System.out.println( "end" );         sleep( 10000 ); }

這裡分配了100M記憶體,為了將結果看清楚,在執行前,執行後分別看看延遲10s,當然你可以根據你的要求自己改改。請提前將OS的資源管理員開啟,看看當前使用的記憶體是多少,如果你是linux當然是看看free或者用top等命令來看;本地程式我是用windows完成,在運行前機器的記憶體如下圖所示:

開始運行在輸入start後,但是未輸出end前,記憶體直接上升將近100m。

在輸入end後發現記憶體立即降低到2.47m,說明回收是有效。

此時可以觀察JVM堆的記憶體,不會有太多的變化,注意:JVM本身啟動後也有一些記憶體開銷,所以不要將那個開銷和這個綁定在一起;這裡之所以一次性申請100m也是為了看清楚過程,其餘的可以做實驗玩玩了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.