UnpooledHeadByteBuf源码分析

来源:互联网 发布:高级软件开发工程师 编辑:程序博客网 时间:2024/05/22 23:29

1.简单介绍

1.UnpooledHeadByteBuf简介
UploadedHeadByteBuf是基于堆内存进行内存分配的字节缓存区,他没有基于对象池的技术实现,意味着每次io的读写都会创建一个新的UnpooledHeadByteBuf,频繁进行大块内存的分配和回收对性能会造成一定的影响,但是相比于堆外内存的申请和释放,他的成本低一点。

相比于PooledHeadbyteBuf,
UnpooledHeadByteBuf的实现原理更加简单,也不容易出现内存管理方面的问题,因此在满足性能的情况下,推荐使用UnpooledHeadByteBuf。

2.1成员变量

 private final ByteBufAllocator alloc; private byte[] array; private ByteBuffer tmpNioBuf;

ByteBufAllocate用于UnpooledHeapByteBuf的内存分配
byte数组作为缓存区
temNioBuf用于实现ByteBuf到ByteBuffer的转换

2.2构造方法

protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int maxCapacity) {        this(alloc, initialArray, 0, initialArray.length, maxCapacity);    }    private UnpooledHeapByteBuf(            ByteBufAllocator alloc, byte[] initialArray, int readerIndex, int writerIndex, int maxCapacity) {        super(maxCapacity);        if (alloc == null) {            throw new NullPointerException("alloc");        }        if (initialArray == null) {            throw new NullPointerException("initialArray");        }        if (initialArray.length > maxCapacity) {            throw new IllegalArgumentException(String.format(                    "initialCapacity(%d) > maxCapacity(%d)", initialArray.length, maxCapacity));        }        this.alloc = alloc;        setArray(initialArray);        setIndex(readerIndex, writerIndex); }

没什么特殊的地方,很容易理解

3.动态扩展缓存。

在前面介绍AbstractByteBuf的时候,有一个setBytes()写操作,我们还提到写的方法与读的方法的不一样的地方在于它能够动态扩展,setBytes()函数中,有一个ensureWritable(int minWritableBytes)函数专门用来检查length:
1.如果长度参数小于零,抛出错误。
2.如果长度小于可读长度(capacity() - writerIndex),老铁没毛病,过去吧。
3.如果长度大于最大可读长度(maxCapacity - writerIndex),对不起,抛出错误。
4.当长度大于可读长度小于最大可读长度,这时候需要动态扩展了

int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);  capacity(newCapacity);

计算最新的容量有ByteBufAllowcator帮我们完成,但是capacity(newCapacity)在AbstractByteBuf中并没有实现,原因是动态扩展要根据ByteBuf是内存的还是堆的来决定。

说了这么多废话,其实就是UnpooledHeadByteBuf这个类和他的兄弟UnpooledDirectByteBuf类都要实现Capacity这个方法,实现的方式不一样而已。
来看他的代码
这里写图片描述
如果新的容量小于零或者大于最大容量,抛出错误。
如果新的容量大于旧的容量,那么直接将新建一个缓存区,将旧的复制到新的中
如果新的容量小于旧的容量,就要进行缩小容量,就是新建一个新的缓存区,判断readIndex和新的容量,如果readIndex大于新的容量,那么直接将readIndex和WriteIndex设置为新的缓存区的readIndex和WriteIndex;
如果readIndex小于新的容量,就要判断writeIndex和新的容量,如果writeIndex大于新的容量,那么直接吧writeIndex设置为新的缓存区的writeIndex,然后进行缓存区的复制。

 System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);

4.读操作方法

 @Override    public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {        checkDstIndex(index, length, dstIndex, dst.capacity());        if (dst.hasMemoryAddress()) {            PlatformDependent.copyMemory(array, index, dst.memoryAddress() + dstIndex, length);        } else if (dst.hasArray()) {            getBytes(index, dst.array(), dst.arrayOffset() + dstIndex, length);        } else {            dst.setBytes(dstIndex, array, index, length);        }        return this;    }    @Override    public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {        checkDstIndex(index, length, dstIndex, dst.length);        System.arraycopy(array, index, dst, dstIndex, length);        return this;    }

checkDstIndex(index, length, dstIndex, dst.capacity());检查函数就不必多说了,比较简单。
首先检查目标数组时候有低位数的内存地址,如果有,直接

 PlatformDependent.copyMemory(array, index, dst.memoryAddress() + dstIndex, length);

然后检查目标有没有array缓存区,(实际上是检查是不是Heap中的吧),如果是就

getBytes(index, dst.array(), dst.arrayOffset() + dstIndex, length);

这个函数实际上就是System.arraycopy(array, index, dst, dstIndex, length)方法,实际上也验证了我的判断:和上面的动态扩展一样,如果是同样来自Heap中的ByteBuf的复制,用这个arraycopy方法就OK啦!

5.写操作

 @Override    public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {        checkSrcIndex(index, length, srcIndex, src.capacity());        if (src.hasMemoryAddress()) {            PlatformDependent.copyMemory(src.memoryAddress() + srcIndex, array, index, length);        } else  if (src.hasArray()) {            setBytes(index, src.array(), src.arrayOffset() + srcIndex, length);        } else {            src.getBytes(srcIndex, array, index, length);        }        return this;    }    @Override    public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {        checkSrcIndex(index, length, srcIndex, src.length);        System.arraycopy(src, srcIndex, array, index, length);        return this;    }

嘿嘿,和读操作一模一样。‘

6.转换成JDK ByteBuffer

public ByteBuffer nioBuffer(int index, int length) {        ensureAccessible();        return ByteBuffer.wrap(array, index, length).slice();    }

一个简单的不能再简单的函数:直接调用ByteBuffer的wrap方法搞定

 public static ByteBuffer wrap(byte[] array,                                    int offset, int length)    {        try {            return new HeapByteBuffer(array, offset, length);        } catch (IllegalArgumentException x) {            throw new IndexOutOfBoundsException();        }    }