ByteBuffer之HeapByteBuffer与DirectByteBuffer

HeapByteBuffer

HeapByteBuffer 是写在jvm堆上面的一个buffer,底层的本质是一个数组,用类封装维护了很多的索引。(limit/position/capacity等),在实际操作时,会把jvm堆上数据拷贝出来到系统内存中,原因是jvm在进行gc(垃圾回收)时,会对数据进行移动,一旦出现这种问题,就会出现数据错乱的情况。

DirectByteBuffer

DirectByteBuffer,底层的数据其实是维护在操作系统的内存中,而不是jvm里,堆外内存,指的时JVM堆以外的内存,这部分内存并不受JVM管控,也就不能被JVM直接回收了。其中DirectByteBuffer里维护了一个引用address指向了数据,从而操作数据,如下:

 private static class Deallocator
        implements Runnable
    {

        private static Unsafe unsafe = Unsafe.getUnsafe();
      //开辟的堆外内存地址指向
        private long address;
}

堆外内存申请内存空间:

//不可以直接被访问
 DirectByteBuffer(int cap) {                  

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
                //调用native方法分配堆外内存,并返回基地址
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }

堆外内存的回收

当直接内存使用完毕,DirectByteBuffer是可以被JVM回收的,但是通过它分配到的堆外内存却不能被JVM回收。那怎么办呢,如果有大量的堆外内存回收不了,就会造成内存泄漏。

堆外内存回收,主要通过两个方面:
从清理器Cleaner入手:
1、从Cleaner的实现得知它是一个虚引用,在从虚引用的定义得知,虚引用必须与一个引用队列相关联,在jvm回收一个对象,发现它有虚引用时,就会把这个对象放入到引用队列中,做一系列操作。

public void clean() {
        if (remove(this)) {
            try {
                this.thunk.run();
            } catch (final Throwable var2) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        if (System.err != null) {
                            (new Error("Cleaner terminated abnormally", var2)).printStackTrace();
                        }

                        System.exit(1);
                        return null;
                    }
                });
            }

        }
    }

clean在Reference弱引用的线程run方法调用

    public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }

2:unsafe释放内存

private static class Deallocator
        implements Runnable
    {

        private static Unsafe unsafe = Unsafe.getUnsafe();

        private long address;
        private long size;
        private int capacity;

        private Deallocator(long address, long size, int capacity) {
            assert (address != 0);
            this.address = address;
            this.size = size;
            this.capacity = capacity;
        }

        public void run() {
            if (address == 0) {
                // Paranoia
                return;
            }
            unsafe.freeMemory(address);
            address = 0;
            Bits.unreserveMemory(size, capacity);
        }

    }

HeapByteBuffer 和 DirectByteBuffer比较

HeapByteBuffer优点:由于内容维护在jvm里,所以把内容写进buffer里速度会快些;并且,可以更容易回收。

DirectByteBuffer优点:跟IO设备打交道时会快很多,因为IO设备读取jvm堆里的数据时,不是直接读取的,而是把jvm里的数据读到一个内存块里,再在这个块里读取的,如果使用DirectByteBuffer,则可以省去这一步,实现零拷贝。

为避免堆外内存造成的内存溢出,通过参数控制:

-XX:MaxDirectMemorySize=500m
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员路同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值