在使用Java NIO時,會常常和ByteBuffer打交道(吐槽下,每次手動flip切換讀寫模式太不友好)。在空Buffer建立時,有兩種方式:java
ByteBuffer.allocateDirect(capacity) ByteBuffer.allocate(capacity)
那麼這兩種Buffer的分配又有什麼不同呢?數組
字面意思,在java heap上分配的內存。此塊內存區域受JVM管理,GC負責回收。使用時無需擔憂Heap Buffer的回收問題。app
堆外內存(說非堆不太準確,畢竟非堆區域不止這一塊),時分配在C Heap上的Buffer,因爲不屬於JVM HEAP,管理/監控起來會比較困難,但也會被GC回收。DirectByteBuffer 自身是(Java)堆內的,它背後真正承載數據的buffer是在(Java)堆外——native memory中的。這是 malloc() 分配出來的內存,是用戶態的。dom
那麼爲何有了Heap Buffer還須要Direct Buffer呢?工具
在JVM的垃圾回收器裏,除了CMS,都是須要移動對象的;若是要把一個Java裏的 byte[] 對象的引用傳給native代碼,讓native代碼直接訪問數組的內容的話,就必需要保證native代碼在訪問的時候這個 byte[] 對象不能被移動,也就是要被「pin」(釘)住。spa
因而就出現了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的話最好仍是複用,避免過多的建立。code
堆外內存不像堆內內存監控那麼簡單,不能直接看堆信息,但能夠經過程序獲取,或者經過一些監控工具來查看orm
注:須要安裝VisualVM-BufferMonitor和VisualVM-MBeans插件對象
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4096); RandomAccessFile rw = new RandomAccessFile(new File("/Users/jiangxin/Desktop/111"), "rw"); MappedByteBuffer map = rw.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 4097); //打印堆外 List<BufferPoolMXBean> pools = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); for (BufferPoolMXBean pool : pools) { System.out.println(pool.getName()); System.out.println(pool.getCount()); System.out.println("memory used " + pool.getMemoryUsed()); System.out.println("total capacity" + pool.getTotalCapacity()); System.out.println(); }
//print direct 1 memory used 4096 total capacity4096 mapped 1 memory used 4097 total capacity4097