Netty AbstractByteBuf逻辑

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 为计数进行倍增,直到倍增后的结果大于或等于需要的容量值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlyingZCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值