0
点赞
收藏
分享

微信扫一扫

Java Debug 笔记:Java中的Heap Buffer与Direct Buffer


前言

在使用Java NIO时,会经常和ByteBuffer打交道(吐槽下,每次手动flip切换读写模式太不友好)。在空Buffer创建时,有两种方式:

ByteBuffer.allocateDirect(capacity)
ByteBuffer.allocate(capacity)

那么这两种Buffer的分配又有什么不一样呢?

Heap Buffer

字面意思,在java heap上分配的内存。此块内存区域受JVM管理,GC负责回收。使用时无需担心Heap Buffer的回收问题。

Direct Buffer

堆外内存(说非堆不太准确,毕竟非堆区域不止这一块),时分配在C Heap上的Buffer,由于不属于JVM HEAP,管理/监控起来会比较困难,但也会被GC回收。DirectByteBuffer 自身是(Java)堆内的,它背后真正承载数据的buffer是在(Java)堆外——native memory中的。这是 malloc() 分配出来的内存,是用户态的。

那么为什么有了Heap Buffer还需要Direct Buffer呢?

在JVM的垃圾回收器里,除了CMS,都是需要移动对象的;如果要把一个Java里的 byte[] 对象的引用传给native代码,让native代码直接访问数组的内容的话,就必须要保证native代码在访问的时候这个 byte[] 对象不能被移动,也就是要被“pin”(钉)住。


如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!​​[Java架构群]​​ 群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的JAVA交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。


于是就出现了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的话最好还是复用,避免过多的创建。

堆外内存的监控

堆外内存不像堆内内存监控那么简单,不能直接看堆信息,但可以通过程序获取,或者通过一些监控工具来查看

jconsole

Java Debug 笔记:Java中的Heap Buffer与Direct Buffer_jvm

jvisualvm

Java Debug 笔记:Java中的Heap Buffer与Direct Buffer_编程语言_02

注:需要安装VisualVM-BufferMonitor和VisualVM-MBeans插件

Java

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

写在最后

最近我整理了整套《JAVA核心知识点总结》,说实话,作为一 名 Java 程序员,不论你需不需要面试都应该好好看下这份资料。拿到手总是不亏的~我的不少粉丝也因此拿到腾讯字节快手offer,​点击下面图片↓直达领取

​​​​

好了,以上就是本文的全部内容了,如果觉得有收获,记得三连,我们下期再见。



举报

相关推荐

0 条评论