【Netty2】 Netty ByteBuf 扩缩容过程分析
ByteBuf 的组成
ByteBuf 是一个抽象类,继承自Object,实现了ReferenceCounted,Comparable。
ReferenceCounted是用于ByteBuf的回收工作,从其名称就能看出,是引用计数法,refCnt是引用数值;其提供了方法retain(),计数+1;release()方法,计数减1,当最终引用计数是0时,资源将被释放。
Comparable主要用作比较。
初始化 ByteBuf 并进行读写
源码 Demo: NettyCapacityDemo.java
数据读写过程中的缓冲区的变化过程:
创建一个非池化的 ByteBuf,大小为30个字节
ByteBuf buf = Unpooled.buffer(30);
ridx: 0, widx: 0, cap: 30
写小于30字节的数据
byte[] bytes = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
buf.writeBytes(bytes);
ridx: 0, widx: 20, cap: 30
读取10位数据
byte b1 = buf.readByte();
byte b2 = buf.readByte();
byte b3 = buf.readByte();
byte b4 = buf.readByte();
byte b5 = buf.readByte();
byte b6 = buf.readByte();
byte b7 = buf.readByte();
byte b8 = buf.readByte();
byte b9 = buf.readByte();
byte b10 = buf.readByte();
ridx: 10, widx: 20, cap: 30
将读取的内容丢弃, discardReadBytes 时出现了内存复制
buf.discardReadBytes();
ridx: 0, widx: 10, cap: 30
清空读写指针
buf.clear();
ridx: 0, widx: 0, cap: 30
写入一段超过容量的内容
此时发生了扩容。扩容后容量从初始的 30 变为 64。此处就是自动进行了扩容操作。
byte[] bytes3 = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36};
buf.writeBytes(bytes3);
ridx: 0, widx: 36, cap: 64
扩容分析
Netty 版本: 4.1.92
找到 AbstractByteBuf 中 writeBytes(byte[] bytes) 方法
public abstract class AbstractByteBuf extends ByteBuf {
@Override
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
// 该方法检查是否有足够的可写空间,是否需要进行扩容
ensureWritable(length);
setBytes(writerIndex, src, srcIndex, length);
writerIndex += length;
return this;
}
@Override
public ByteBuf writeBytes(byte[] src) {
writeBytes(src, 0, src.length);
return this;
}
}
AbstractByteBuf 中的 ensureWritable0 扩容方法:
public abstract class AbstractByteBuf extends ByteBuf {
... ...
final void ensureWritable0(int minWritableBytes) {
//获取当前写入下标
final int writerIndex = writerIndex();
//预计写入后的下标
final int targetCapacity = writerIndex + minWritableBytes;
//如果小于初始容量,不扩容
// using non-short-circuit & to reduce branching - this is a hot path and targetCapacity should rarely overflow
if (targetCapacity >= 0 & targetCapacity <= capacity()) {
ensureAccessible();
return;
}
//超过最大容量,跑出异常
if (checkBounds && (targetCapacity < 0 || targetCapacity > maxCapacity)) {
ensureAccessible();
throw new IndexOutOfBoundsException(String.format(
"writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
writerIndex, minWritableBytes, maxCapacity, this));
}
// 计算能写入的大小的最大值
// Normalize the target capacity to the power of 2.
final int fastWritable = maxFastWritableBytes();
//如果能写入的最大值 大于 需要写入的容量,则将能写入的值加上当前写入下标做为新的容量;
//否则使用calculateNewCapacity去分配
int newCapacity = fastWritable >= minWritableBytes ? writerIndex + fastWritable
:
// 计算新的容量的方法是调用的 ByteBufAllocator 的 calculateNewCapacity()方法。 这里的 ByteBufAllocator的实现类是 AbstractByteBufAllocator
alloc().calculateNewCapacity(targetCapacity, maxCapacity);
// Adjust to the new capacity.
capacity(newCapacity);
}
}
计算能写入的大小的最大值
// maxFastWritableBytes内部会获取一个maxLength,这个值是在allocate缓冲区是计算出来的2的幂,且小于512的,用它减去当前写入index的位置。
abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
public int maxFastWritableBytes() {
return Math.min(maxLength, maxCapacity()) - writerIndex;
}
}
calculateNewCapacity 计算新的容量的方法
public abstract class AbstractByteBufAllocator implements ByteBufAllocator {
... ...
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));
}
// 扩容的阈值,4兆字节大小。 CALCULATE_THRESHOLD = 1048576 * 4
final int threshold = CALCULATE_THRESHOLD; // 4 MiB page
//如果需要的容量等于页的阈值,就返回该阈值
if (minNewCapacity == threshold) {
return threshold;
}
// 如果要扩容后新的容量大于页的阈值,那么扩容的方式改为用新的容量加上阈值; 否则将新容量改为双倍大小进行扩容
// If over threshold, do not double but just increase by threshold.
if (minNewCapacity > threshold) {
// 计算最小需要几个页阈值的大小,并赋予一个新的容量值
int newCapacity = minNewCapacity / threshold * threshold;
// 如果新容量,比 最大容量 - 一个分页还要大,即剩余不到一个页,则赋予最大容量
if (newCapacity > maxCapacity - threshold) {
newCapacity = maxCapacity;
} else {
// 在赋予的新容量基础上 在加一个页容量
newCapacity += threshold;
}
return newCapacity;
}
// 如果要扩容后新的容量没有超过页阈值(小于4兆字节),则从64字节开始扩容,每次双倍扩容,直到小于指定的新容量位置
// 64 <= newCapacity is a power of 2 <= threshold
final int newCapacity = MathUtil.findNextPositivePowerOfTwo(Math.max(minNewCapacity, 64));
// 取新赋予容量newCapacity 和最大值maxCapacity 中的最小值
return Math.min(newCapacity, maxCapacity);
}
}