ByteBuffer有兩種一種是heap ByteBuffer,該類對象分配在JVM的堆內存里面,直接由Java虛擬機負責垃圾回收,一種是direct ByteBuffer是通過jni在虛擬機外內存中分配的。通過jmap無法查看該快內存的使用情況。只能通過top來看它的內存使用情況。
JVM堆內存大小可以通過-Xmx來設置,同樣的direct ByteBuffer可以通過-XX:MaxDirectMemorySize來設置,此參數的含義是當Direct ByteBuffer分配的堆外內存到達指定大小后,即觸發Full GC。注意該值是有上限的,默認是64M,最大為sun.misc.VM.maxDirectMemory(),在程序中中可以獲得-XX:MaxDirectMemorySize的設置的值。
@Test public void testBits() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // System.out.println("maxMemoryValue:"+sun.misc.VM.maxDirectMemory()); ByteBuffer buffer=ByteBuffer.allocateDirect(0); Class c = Class.forName("java.nio.Bits"); Field maxMemory = c.getDeclaredField("maxMemory"); maxMemory.setAccessible(true); synchronized (c) { Long maxMemoryValue = (Long)maxMemory.get(null); System.out.println("maxMemoryValue:"+maxMemoryValue); } }
下面要談到垃圾回收機制:direct ByteBuffer通過full gc來回收內存的,direct ByteBuffer會自己檢測情況而調用system.gc(),但是如果參數中使用了DisableExplicitGC那么就無法回收該快內存了,-XX:+DisableExplicitGC標志自動將System.gc()調用轉換成一個空操作,就是應用中調用System.gc()會變成一個空操作。那么如果設置了就需要我們手動來回收內存了
@Test public void testAllocateDirector() throws Exception{ ByteBuffer buffer=ByteBuffer.allocateDirect(1024); Field cleanerField = buffer.getClass().getDeclaredField("cleaner"); cleanerField.setAccessible(true); Cleaner cleaner = (Cleaner) cleanerField.get(buffer); cleaner.clean(); }
那么除了FULL GC還有別的能回收direct ByteBuffer嗎?CMS GC會回收Direct ByteBuffer的內存,CMS主要是針對old space空間的垃圾回收。但是是Oracle JDK 6u32以后的版本
講了這么多談下使用場景
1:多用網絡編程中用到,實現zero copy,數據不需要再native memory和jvm memory中來回copy
2:由於造和析構Direct Buffer時間成本高,建議使用緩沖池,參見netty的實現