PooledByteBuf扩容操作
我们上面分析了分配操作和回收操作,部分代码我们在上面已经分析过了,现在我们看下扩容操作。
final void ensureWritable0(int minWritableBytes) {
ensureAccessible();
//如果可以写入的空间比需要写入的空间大直接返回
if (minWritableBytes <= writableBytes()) {
return;
}
if (checkBounds) {
//超出了最大可写大小
if (minWritableBytes > maxCapacity - writerIndex) {
throw new IndexOutOfBoundsException(String.format(
"writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
writerIndex, minWritableBytes, maxCapacity, this));
}
}
//计算扩容后的值
int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
// 扩容
capacity(newCapacity);
}
在AbstractByteBuf中会有很多调用ensureWritable0的操作,判断需不需要扩容,我们跟踪到这个方法中,如果需要写入的空间比可写内存小则直接返回,不需要扩容,如果不是则重新计算需要扩容的长度
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
if (minNewCapacity == threshold) {
return threshold;
}
if (minNewCapacity > threshold) {//如果大于4M
int newCapacity = minNewCapacity / threshold * threshold;//向上取整
if (newCapacity > maxCapacity - threshold) {
newCapacity = maxCapacity;
} else {
newCapacity += threshold;
}
return newCapacity;
}
//如果没有达到4M,则以64B开始每次翻一倍扩展
int newCapacity = 64;
while (newCapacity < minNewCapacity) {
newCapacity <<= 1;
}
return Math.min(newCapacity, maxCapacity);
}
这里会先判断需要扩容的大小不能超过最大值,如果大小超过了4M,则每次向上取整获取扩容的大小,如果小于4M,则以64开始每次翻一倍,获取大于minNewCapacity的扩容值。我们继续跟踪
public final ByteBuf capacity(int newCapacity) {
checkNewCapacity(newCapacity);
// If the request capacity does not require reallocation, just update the length of the memory.
//如果不是池化的
//如果请求容量不需要重新分配,则只需更新内存的长度即可
if (chunk.unpooled) {
if (newCapacity == length) {
return this;
}
} else {
//如果需要分配长度大于已经分配的长度,但是不大于最大可分配的长度,直接返回
if (newCapacity > length) {
if (newCapacity <= maxLength) {
length = newCapacity;
return this;
}
} else if (newCapacity < length) {
//如果新的长度小于length
if (newCapacity > maxLength >>> 1) {
if (maxLength <= 512) {//tiny类型
if (newCapacity > maxLength - 16) {
length = newCapacity;
//重新设置读写索引
setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));
return this;
}
} else { // > 512 (i.e. >= 1024)
length = newCapacity;
//重新设置读写索引
setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));
return this;
}
}
} else {
return this;
}
}
// Reallocation required.
//需要重新分配
chunk.arena.reallocate(this, newCapacity, true);
return this;
}
这里会根据不同情况判断需要不需要扩容,如果需要扩容则执行reallocate方法
void reallocate(PooledByteBuf<T> buf, int newCapacity, boolean freeOldMemory) {
if (newCapacity < 0 || newCapacity > buf.maxCapacity()) {
throw new IllegalArgumentException("newCapacity: " + newCapacity);
}
int oldCapacity = buf.length;
if (oldCapacity == newCapacity) {
return;
}
PoolChunk<T> oldChunk = buf.chunk;
ByteBuffer oldNioBuffer = buf.tmpNioBuf;
long oldHandle = buf.handle;
T oldMemory = buf.memory;
int oldOffset = buf.offset;
int oldMaxLength = buf.maxLength;
int readerIndex = buf.readerIndex();
int writerIndex = buf.writerIndex();
//重新申请newCapacity大小内存
allocate(parent.threadCache(), buf, newCapacity);
if (newCapacity > oldCapacity) {
memoryCopy(
oldMemory, oldOffset,
buf.memory, buf.offset, oldCapacity);
} else if (newCapacity < oldCapacity) {
//设置写索引
if (readerIndex < newCapacity) {
if (writerIndex > newCapacity) {
writerIndex = newCapacity;
}
//内存赋值
memoryCopy(
oldMemory, oldOffset + readerIndex,
buf.memory, buf.offset + readerIndex, writerIndex - readerIndex);
} else {
readerIndex = writerIndex = newCapacity;
}
}
//设置读写索引
buf.setIndex(readerIndex, writerIndex);
//释放原来的分配的内存
if (freeOldMemory) {
free(oldChunk, oldNioBuffer, oldHandle, oldMaxLength, buf.cache);
}
}
这里很多方法我们在上面已经分析过了,这里就不再详细分析