UnpooledHeapByteBuf类
UnpooledHeapByteBuf类的类图如下所示,UnpooledHeapByteBuf继承自AbstractReferenceCountedByteBuf类,UnpooledHeapByteBuf是一个基于JVM堆内存进行内存分配的缓冲区,是一个非池化的实现,所以在每次IO读写的时候都会创建一个UnpooledHeapByteBuf对象。基于堆内存的缓冲区的特点是分配和销毁速度很快,但是在进行IO读写的时候多一次从内核空间向用户空间复制的过程,故其在IO读写的场景下性能较低。
UnpooledHeapByteBuf对字节数据的存储数据是大端顺序
属性
ByteBuf的分配器,ByteBufAllocator接口的所有实现类都是现成安全的
private final ByteBufAllocator alloc;
byte[] array;//支撑数组
private ByteBuffer tmpNioBuf;//JDK Nio包中的ByteBuffer对象
构造函数
/**
* alloc:ByteBuf分配器
* initialCapacity:缓冲区的初始化容量
* maxCapacity:缓冲区的最大容量
*
*/
public UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
super(maxCapacity);//设置缓冲区的最大容量
if (initialCapacity > maxCapacity) {//校验参数的合法性
throw new IllegalArgumentException(String.format(
"initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
}
this.alloc = checkNotNull(alloc, "alloc");//非空校验
setArray(allocateArray(initialCapacity));//开辟数组并将其赋给缓冲区对象
setIndex(0, 0);//设置缓冲区的读写索引
}
/**
* alloc:ByteBuf分配器
* initialArray:缓冲区的初始化数组
* maxCapacity:缓冲区的最大容量
*
*/
protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int maxCapacity) {
super(maxCapacity);//设置缓冲区的最大容量
checkNotNull(alloc, "alloc");//非空校验
checkNotNull(initialArray, "initialArray");//非空校验
if (initialArray.length > maxCapacity) {//校验参数的合法性
throw new IllegalArgumentException(String.format(
"initialCapacity(%d) > maxCapacity(%d)", initialArray.length, maxCapacity));
}
this.alloc = alloc;
setArray(initialArray);
setIndex(0, initialArray.length);//设置缓冲区的读写索引
}
allocateArray(int initialCapacity)和setArray(byte[] initialArray)方法如下:
protected byte[] allocateArray(int initialCapacity) {
return new byte[initialCapacity];
}
private void setArray(byte[] initialArray) {
array = initialArray;
tmpNioBuf = null;
}
设置缓冲区的容量
UnpooledHeapByteBuf#capacity(int newCapacity)方法的大致逻辑如下:
1、如果newCapacity == oldCapacity则不用做任何操作,直接返回
2、如果newCapacity > oldCapacity,表示需要做扩容操作,执行扩容操作时,读写索引索引不需要被重置,只需要执行数组拷贝即可
3、如果newCapacity > oldCapacity,表示需要做缩容操作,此时需要再做如下判断
3.1如果newCapacity >= writerIndex,则不需要修改读写索引,直接执行数组复制即可
如下图所示:无需修改读写索引,直接进行数组拷贝操作即可
3.2 如果newCapacity < writerIndex,将写索引设置为newCapacity,读索引为newCapacity和readerIndex二者的最小值
如下图所示,newCapacity < writerIndex,此时需要重新设置读写指针
执行读写指针重置并拷贝数组后得到的新的数组如下图橙色部分所示:
@Override
public ByteBuf capacity(int newCapacity) {
checkNewCapacity(newCapacity);
byte[] oldArray = array;
int oldCapacity = oldArray.length;
if (newCapacity == oldCapacity) {
return this;
}
int bytesToCopy;
if (newCapacity > oldCapacity) {
bytesToCopy = oldCapacity;
} else {
trimIndicesToCapacity(newCapacity);
bytesToCopy = newCapacity;
}
byte[] newArray = allocateArray(newCapacity);
System.arraycopy(oldArray, 0, newArray, 0, bytesToCopy);//native方法
setArray(newArray);
freeArray(oldArray);
return this;
}
字节数组的复制
UnpooledHeapByteBuf类对于字节数组的复制的实现是直接调本地方法System.arraycopy完成的
@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;
}
将字节数组转换为jdk nio包下的ByteBuffer
Netty的实现也是调用ByteBuffer的wrap方法完成的,与ByteBuffer不同的是netty又调用的slice()方法,ByteBuffer#slice()方法的作用是创建一个源缓冲区的浅拷贝,从源缓冲区的position开始。
@Override
public ByteBuffer nioBuffer(int index, int length) {
ensureAccessible();
return ByteBuffer.wrap(array, index, length).slice();
}
释放缓冲区的实现
@Override
protected void deallocate() {
freeArray(array);
array = EmptyArrays.EMPTY_BYTES;
}