在使用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