【Netty2】 Netty ByteBuf 扩缩容过程分析

【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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值