阅读须知
- Netty版本:4.1.14.Final
- 文章中使用/* */注释的方法会做深入分析
正文
建议首先阅读Netty源码解析之AbstractByteBuf,UnpooledHeapByteBuf是AbstractByteBuf的子类,它是基于内存的非池化的字节缓冲区,我们首先来看它的主要成员变量:
UnpooledHeapByteBuf:
// 用于内存分配
private final ByteBufAllocator alloc;
// 字节数组缓冲区
byte[] array;
// 用于实现ByteBuf到JDK ByteBuffer的转换
private ByteBuffer tmpNioBuf;
我们在分析AbstractByteBuf的读写操作时看到有一些操作是需要子类实现的,其中读操作readBytes方法中会调用需要子类实现的getBytes方法来完成复制字节到目标数组的操作,我们来看实现:
UnpooledHeapByteBuf:
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
// 检查数组下标和容量
checkDstIndex(index, length, dstIndex, dst.length);
// 从原数组复制length个字节到目标字节数组中
System.arraycopy(array, index, dst, dstIndex, length);
return this;
}
很简单,除了校验之外就是使用JDK的API System.arraycopy来完成复制操作。同样,写操作writeBytes也需要子类实现setBytes方法来完成将字节复制到缓冲区的操作:
UnpooledHeapByteBuf:
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
// 检查数组下标和容量
checkSrcIndex(index, length, srcIndex, src.length);
// 将指定字节数组从srcIndex下标开始复制length长度到缓冲区中
System.arraycopy(src, srcIndex, array, index, length);
return this;
}
与读操作非常相似,只不过是数组复制的方向是相反的。我们在介绍AbstractByteBuf的writeBytes方法时提到,在缓冲区容量不够时是可以动态扩容的,计算过后调整容量的操作是由子类来完成的,我们来看实现:
UnpooledHeapByteBuf:
public ByteBuf capacity(int newCapacity) {
// 检查缓冲区是否被释放,新的容量不能小于0且新容量不能大于最大容量
checkNewCapacity(newCapacity);
int oldCapacity = array.length;
byte[] oldArray = array;
// 新容量大于旧容量的情况,直接拷贝和替换旧数组即可
if (newCapacity > oldCapacity) {
// 新容量创建新字节数组
byte[] newArray = allocateArray(newCapacity);
// 将旧字节数组中的内容拷贝到新数组中
System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
// 将数组设置为新数组,将用于软换为JDK ByteBuffer的tmpNioBuf变量置为null
setArray(newArray);
// 子类实现释放旧数组操作,默认空实现
freeArray(oldArray);
// 新容量小于旧容量的情况,需要考虑读写索引的设置和拷贝的长度
} else if (newCapacity < oldCapacity) {
// 新容量创建新字节数组
byte[] newArray = allocateArray(newCapacity);
int readerIndex = readerIndex();
// 读索引小于新容量的情况
if (readerIndex < newCapacity) {
int writerIndex = writerIndex();
if (writerIndex > newCapacity) {
// 写索引大于新容量,将写索引置为新容量
writerIndex(writerIndex = newCapacity);
}
// 从读索引开始,从旧数组中拷贝写索引减读索引也就是未读的部分到新数组中
System.arraycopy(oldArray, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
} else {
// 这里说明读写索引都不小于新容量,所以直接将读写索引设置为新容量
setIndex(newCapacity, newCapacity);
}
// 将数组设置为新数组,将用于软换为JDK ByteBuffer的tmpNioBuf变量置为null
setArray(newArray);
// 子类实现释放旧数组操作,默认空实现
freeArray(oldArray);
}
return this;
}
最后我们来看一下如何转换为JDK的ByteBuffer:
UnpooledHeapByteBuf:
public ByteBuffer nioBuffer(int index, int length) {
ensureAccessible();
return ByteBuffer.wrap(array, index, length).slice();
}
很简单,调用JDK NIO提供的API来完成转换JDK ByteBuffer的操作。到这里UnpooledHeapByteBuf的源码分析就完成了。