3)扩容机制
A ByteBuffer的存储
ByteBuffer在put数据时,会校验剩余空间是否不足,如果不足,会抛出异常。
ByteBuffer buffer = ByteBuffer.allocate(8); buffer.put("yu".getBytes()); ---------------------------------------------------- public final ByteBuffer put(byte[] src) { return put(src, 0, src.length); } // 额外接收偏移量(存储数据的起始位置) 和数据长度 public ByteBuffer put(byte[] src, int offset, int length) { // 校验参数的有效性 checkBounds(offset, length, src.length); // 如果要存储数据的长度 > 剩余可用空间 抛出buffer越界的异常 if (length > remaining()) throw new BufferOverflowException(); // 如果剩余空间足够 计算存储的结束位置 = 偏移量 + 数据长度 int end = offset + length; for (int i = offset; i < end; i++) this.put(src[i]); return this; }
如果要手动对ByteBuffer扩容,可以在put之前,先校验剩余空间是否足够,如果不足够,创建一个新的ByteBuffer,新的容量确保足够,旧的buffer数据拷贝到新的buffer中,然后继续存储数据。
B ByteBuf的存储和扩容
当写数据时,先判断是否需要扩容,如果当前空间较小(<4M),以64作为基数倍增(10 -> 64 -> 128 -> 256), 如果当前空间较大(>4M), 每次扩容都增加4M,这种方式叫做"步进式"。
验证扩容
public static void main(String[] args) { ByteBuf buf = Unpooled.buffer(10); System.out.println("capacity:" + buf.capacity()); for (int i = 0; i < 11; i++) { buf.writeByte(i); } System.out.println("capacity:" + buf.capacity()); for (int i = 0; i < 65; i++) { buf.writeByte(i); } System.out.println("capacity:" + buf.capacity()); }
输出结果 可以看到 扩容的范围变化
capacity:10
capacity:64
capacity:128
查看源码,以AbstractByteBuf子类为依据查看,最重要的子类之一,ByteBuf的公共属性和功能都在此中实现。 ByteBuf buf = Unpooled.buffer(10); System.out.println("capacity: " + buf.capacity()); for (int i = 0; i < 11; i++) { buf.writeByte(i); } ---------------------------------------------------- [ByteBuf类] public abstract ByteBuf writeByte(int value); 按住Ctrl+Alt快捷键 [AbstractByteBuf子类] ---------------------------------------------------- @Override public ByteBuf writeByte(int value) { // 确保可写空间足够 ensureWritable0(1); // 写入数据 _setByte(writerIndex++, value); return this; } // 参数为 最小写入数据的大小 final void ensureWritable0(int minWritableBytes) { final int writerIndex = writerIndex(); // 目标容量 = 当前写操作索引 + 最小写入数据大小 final int targetCapacity = writerIndex + minWritableBytes; // 容量足够 不需扩容 if (targetCapacity <= capacity()) { ensureAccessible(); return; } // 容量不足时 如果目标容量 超出最大容量 抛出异常 if (checkBounds && targetCapacity > maxCapacity) { ensureAccessible(); throw new IndexOutOfBoundsException(String.format( "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s", writerIndex, minWritableBytes, maxCapacity, this)); } // 扩容逻辑 // 获取可写空间大小 final int fastWritable = maxFastWritableBytes(); // 如果 可写空间 >= 所需空间 新的容量=写操作索引+可写空间大小 // 如果 可写空间 < 所需空间 计算要扩容的新容量大小 calculateNewCapacity方法 int newCapacity = fastWritable >= minWritableBytes ? writerIndex + fastWritable : alloc().calculateNewCapacity(targetCapacity, maxCapacity); // Adjust to the new capacity. // 计算完成后 生成新的ByteBuffer capacity(newCapacity); } // 获取可写空间大小 public int maxFastWritableBytes() { return writableBytes(); } [AbstractByteBufAllocator子类] ---------------------------------------------------- // 计算要扩容的新容量大小 @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)); } // 扩容方式的分界点 以4M大小为界 final int threshold = CALCULATE_THRESHOLD; // 4 MiB page if (minNewCapacity == threshold) { return threshold; } // If over threshold, do not double but just increase by threshold.、 // 如果所需容量大于4M 按照步进的方式扩容 // 举例: 比如 minNewCapacity = 5M if (minNewCapacity > threshold) { // newCapacity = 5 / 4 * 4 = 4M 确保是4的倍数 int newCapacity = minNewCapacity / threshold * threshold; if (newCapacity > maxCapacity - threshold) { newCapacity = maxCapacity; } else { // newCapacity = 4 + 4 = 8M; newCapacity += threshold; } return newCapacity; } // Not over threshold. Double up to 4 MiB, starting from 64. // 如果所需容量大于4M 按照64的倍数扩容 找到最接近所需容量的64的倍数 int newCapacity = 64; while (newCapacity < minNewCapacity) { newCapacity <<= 1; } // 保障在最大可接受容量范围内 return Math.min(newCapacity, maxCapacity); }
4)优势
A 池化的方式提高内存使用率
B 提出了复合型缓冲区的整合方案
C 增加了索引,使读写分离,使用更便捷
D 解决了ByteBuffer长度固定的问题,增加了扩容机制
E 用引用计数的方式进行对象回收