Direct Buffer和flip方法 和垃圾收集

DirectBuffer和MappedByteBuffer

DirectBuffer : 在 Buffer 的方法定义中,有一个 isDirect() 方法,返回当前方法是否是 Direct 类型。这是 Java 提供的堆外 Buffer。可以使用 allocateDirect 方法直接创建。如:ByteBuffer.allocateDirect(1024);
MappedByteBuffer:它将文件按照指定大小直接映射为内存区域,当程序访问这个内存区域时,将直接操作这块文件数据,省去了将数据从内核空间向用户空间传输的损耗。我们可以使用 FileChannel.map 创建 MappedByteBuffer,它本质上也是种 Direct Buffer。

为甚需要directBuffer

在实际使用中,Java 会尽量对 Direct Buffer 仅作本地 IO 操作,对于很大数据量的 IO 密集型操作,可能会带来很大的性能优势,因为:
Direct Buffer 在生命周期内内存地址都不会再做改变,进而内核可以直接安全地对其访问,很多 IO 操作会很高效。
Direct Buffer 避免了堆内对象需要的额外的维护工作,提高了效率。

典型的nio中,使用了directBuffer可以避免用户空间和内核空间内存的拷贝,提升性能。

合理使用directBuffer:

但是,高效背后也是高成本。Direct Buffer 在创建和销毁过程中,都会比一般的 Buffer 增加部分开销,所以通常应该用于长期使用、数据量较大的场景。并且可以使用buffer池。可以参考netty的使用

directBuffer实例代码与flip方法

    public static boolean directCopyFile(FileInputStream fis, FileOutputStream fos) throws IOException {
        FileChannel srcFileChannel = fis.getChannel();
        FileChannel targetFileChannel = fos.getChannel();
        //间接获取ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        while(true) {
            byteBuffer.clear();
            int readByte = srcFileChannel.read(byteBuffer);
            if(readByte == -1) {
                break;
            }
            byteBuffer.flip();
            targetFileChannel.write(byteBuffer);
        }
        return true;
    }

buffer中的flip方法涉及到bufer中的Capacity,Position和Limit三个概念。
Capacity在读写模式下都是固定的,就是我们分配的缓冲大小;
Position类似于读写指针,表示当前读(写)到什么位置
Limit在写模式下表示最多能写入多少数据,在读模式下表示最多能读多少数据,初始和Capacity相同。

在写模式下调用flip方法,那么limit就设置为了position当前的值(即当前写了多少数据),postion会被置为0,以表示读操作从缓存的头开始读。也就是说调用flip之后,读写指针指到缓存头部,并且设置了最多只能读出之前写入的数据长度(而不是整个缓存的容量大小)。

directBuffer的垃圾回收

Direct Buffer 因为不在堆上,所以 Xmx 参数对它无效,可以使用下面的代码设置堆外内存的大小:
-XX:MaxDirectMemorySize=512M
从参数设置和内存问题排查来看,我们在设置 JVM 需要的内存时,如果用到了堆外内存,还应考虑堆外内存的开销。而出现了 OOM 问题时,也应该考虑是否是堆外内存不够的可能性。

对于 Direct Buffer 的回收,可以考虑:

在应用程序中,显式调用 System.gc() 来强制触发。
另一种思路是,在大量使用 Direct Buffer 的部分框架中,框架会自己在程序中调用释放方法,Netty 就是这么做的。
重复使用 Direct Buffer,而不是每次需要再创建,用完立刻销毁。

跟踪诊断 Direct Buffer 的内存占用的方法
在普通的垃圾收集日志中,并不包含 Direct Buffer 等信息,所以 Direct Buffer 的内存诊断是个比较头疼的问题。在 java 8 以后,我们可以使用 Native Memory Tracking (NMT) 来诊断,在启动程序时加上下面的参数可以激活 NMT,但是会导致 JVM 出现 5%~10% 的性能下降:

-XX:NativeMemoryTracking={summary|detail}
开启 NMT 后,就可以通过下面的命令进行交互式对比:

// 打印 NMT 信息
jcmd <pid> VM.native_memory detail 
// 进行 baseline,以对比分配内存变化
jcmd <pid> VM.native_memory baseline
// 进行 baseline,以对比分配内存变化
jcmd <pid> VM.native_memory detail.diff

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值