AbstractByteBuf逻辑
AbstractByteBuf 继承自 ByteBuf,ByteBuf 的一些公共属性和功能会在 AbstractByteBuf 中实现.
主要成员变量
// 所有 ByteBuf共用该属性,leakDetector用于检测对象是否泄漏
static final ResourceLeakDetector<ByteBuf> leakDetector =
ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class);
int readerIndex;
int writerIndex;
private int markedReaderIndex;
private int markedWriterIndex;
private int maxCapacity;
读索引、写索引、mark、最大容量等公共属性定义.
ResourceLeakDetector 用于检测对象是否泄漏.
在 AbstractByteBuf 中并没有定义 ByteBuf 的缓冲区实现,由具体子类实现.
读操作
读操作以及其他的一些公共功能都由父类实现,差异化功能由子类实现
public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
checkReadableBytes(length); // 先对缓冲区的可用空间进行校验
getBytes(readerIndex, dst, dstIndex, length); // 该方法由子类具体实现. 从当前的读索引开始, 复制 length 个字节到目标 byte 数组中
readerIndex += length; // 如果读取成功,需要对读索引进行递增:readerIndex+=length
return this;
}
如果读取的长度小于 0,则抛出 IllegalArgumentException 异常提示参数非法;
如果可写的字节数小于需要读取的长度,则抛出 IndexOutOfBoundsException 异常。
校验通过之后,调用 getBytes 方法,从当前的读索引开始,复制 length 个字节到目标 byte 数组中。由于不同的子类复制操作的技术实现细节不同,因此该方法由子类实现
如果读取成功,需要对读索引进行递增:readerIndex+=length
写操作
与读取操作类似,写操作的公共行为在 AbstractByteBuf 中实现
writeBytes(byte[]src,int srcIndex,int length)
将源字节数组中从 srcIndex 开始,到 srcIndex+length 截止的字节数组写入到当前的 ByteBuf 中。
@Override
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
ensureWritable(length);
setBytes(writerIndex, src, srcIndex, length);
writerIndex += length;
return this;
}
writeByte 逻辑
首先对写入字节数组的长度进行合法性校验
如果写入的字节数组长度小于 0,则抛出 IllegalArgumentException 异常;
如果写入的字节数组长度小于当前 ByteBuf 可写的字节数,说明可以写入成功,直接返回;
如果写入的字节数组长度大于可以动态扩展的最大可写字节数,说明缓冲区无法写入超过其最大容量的字节数组,抛出 IndexOutOfBoundsException 异常。
如果当前写入的字节数组长度虽然大于目前 ByteBuf 的可写字节数,但是通过自身的动态扩展可以满足新的写入请求,则进行动态扩展。
calculateNewCapacity() 逻辑
@Override
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
checkPositiveOrZero(minNewCapacity, "minNewCapacity");
if (minNewCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
minNewCapacity, maxCapacity));
}
final int threshold = CALCULATE_THRESHOLD; // 4 MiB page 首先设置门限阈值为4MB
if (minNewCapacity == threshold) { // 当需要的新容量正好等于门限阈值时,使用阈值作为新的缓冲区容量。
return threshold;
}
// If over threshold, do not double but just increase by threshold.
if (minNewCapacity > threshold) { // 如果新申请的内存空间大于阈值
int newCapacity = minNewCapacity / threshold * threshold; // newCapacity为扩容的增量. 采用每次步进4MB的方式进行内存扩张
if (newCapacity > maxCapacity - threshold) { // 最大不能超过 maxCapacity. 扩张的时候需要对扩张后的内存和最大内存(maxCapacity)进行比较,如果大于缓冲区的最大长度,则使用maxCapacity作为扩容后的缓冲区容量。
newCapacity = maxCapacity;
} else {
newCapacity += threshold; // 扩容后的 capacity
}
return newCapacity;
}
// 如果扩容后的新容量小于阈值,则以64为计数进行倍增,直到倍增后的结果大于或等于需要的容量值。
// Not over threshold. Double up to 4 MiB, starting from 64.
int newCapacity = 64;
while (newCapacity < minNewCapacity) {
newCapacity <<= 1;
}
return Math.min(newCapacity, maxCapacity);
}
首先设置阈值为 4MB,当需要的新容量正好等于门限阈值时,使用阈值作为新的缓冲区容量。
如果新申请的内存空间大于阈值,不能采用倍增的方式(防止内存膨胀和浪费)扩张内存,而采用每次步进 4MB 的方式进行内存扩张。
扩张的时候需要对扩张后的内存和最大内存(maxCapacity)进行比较,如果大于缓冲区的最大长度,则使用 maxCapacity 作为扩容后的缓冲区容量。
如果扩容后的新容量小于阈值,则以 64 为计数进行倍增,直到倍增后的结果大于或等于需要的容量值。