netty的byteBuf

1.byteBuf是什么
byteBuf是netty对nio的byteBuffer的现实,因为byteBuff使用场景有限

2.byteBuf类关系图

3.为什么netty要用bytebuf来替换bytebuffer
1.使用readIndex和writeIndex来解决每次读写时需要进行flip操作的问题
简化了读写的操作,读有读的指针,写有写的指针,互补干扰
2.封装了每次put需要进行可写空间的检测,以及超出可写空间的异常
用户无需去写多余的检测,并且netty会根据写入内容大小自动扩容
3.提供了不同场景下使用堆内存和直接内存的方式来提高效率
4.提供了更多方便的api

3.byteBuf能做什么
headByteBuf:堆内存,可以快速创建和回收,需要多一次数据的复制(到jvm中)
directByteBuf:直接内存,效率高,分配和回收效率慢

java本身的buteBuffer已经可以实现以下操作:
7种buffer的读写、缓冲区的创建、本身的copy和slice、设置网络字节序、操作数据位置指针

netty如何封装:
首先介绍byteBuf的几个关键元素
readIndex:读的位置
writeIndex:写的位置
capocity:缓冲区大小
discardable bytes:已经完成编解码的空间,用于在write时自动扩容
readable bytes:可读空间大小,超出这个缓存空间会抛异常
writable bytes:可写空间大小,查出会利用discardable bytes的空间进行扩容,后面会具体介绍

4.AbstractByteBuf能做什么
maxcapacity:最大容量
markReaderIndex:标记回滚的读指针
markWriterIndex:标记回滚的写指针
SwappedByteBuf:与本 ByteBuf 大小端属性相反的 ByteBuf

有效性检查,如果引用计数 refCnt 为 0,表示该 ByteBuf 已经被回收,不能再写入。
输入参数有效性检查:要写入的数据量不能小于 0,写入之后总数据量也不能大于最大容量。
当容量不足时,如果尚未超过最大容量,则进行扩容。
修改写指针。

public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
    ensureAccessible();
    ensureWritable(length);
    // setBytes 交给子类实现。
    setBytes(writerIndex, src, srcIndex, length);//修改指针
    writerIndex += length;
    return this;
}

protected final void ensureAccessible() {
    if (refCnt() == 0) {//代表buf已经被销毁
        throw new IllegalReferenceCountException(0);
    }
}

 public ByteBuf ensureWritable(int minWritableBytes) {
    if (minWritableBytes < 0) {//要写内容小于0,代表没有数据可写
        throw new IllegalArgumentException(String.format(
                "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
    }

    if (minWritableBytes <= writableBytes()) {
        return this;//如果要写的内容大小小于可写的容量,则不需要扩容
    }

    if (minWritableBytes > maxCapacity - writerIndex) {
        //要写的内容不可超过可写最大容量
        throw new IndexOutOfBoundsException(String.format(
                "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                writerIndex, minWritableBytes, maxCapacity, this));
    }

    // Normalize the current capacity to the power of 2.
    // 翻倍扩容
    int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);

    // 具体扩容操作由子类实现。
    capacity(newCapacity);
    return this;
}    

1.计算可写缓存

2.具体的扩容是由不同的子类来实现的

5.AbstractReferrenceCountedByteBuf
类似于 JVM 内存回收的对象引用计数器,用于跟踪对象的分配和回收,实现手动控制内存回收
refCnt:记录对象引用次数。
refCntUpdater:用于对 refCnt 进行原子更新

private static final AtomicIntegerFieldUpdater refCntUpdater;

static {
    AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> updater =
            PlatformDependent.newAtomicIntegerFieldUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
    if (updater == null) {
        updater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
    }
    refCntUpdater = updater;
}

private volatile int refCnt = 1;

引用加一retain(),引用减一release()方法与添加基本一致
public ByteBuf retain() {
for (;? {
int refCnt = this.refCnt;
if (refCnt == 0) {
throw new IllegalReferenceCountException(0, 1);
}
if (refCnt == Integer.MAX_VALUE) {
throw new IllegalReferenceCountException(Integer.MAX_VALUE, 1);
}
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
break;
}
}
return this;
}

release()
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
if (refCnt == 1) {
deallocate();
return true;
}
return false;
}

6.UnpooledHeapByteBuf 堆缓冲区内存管理的byteBuf
final ByteBufAllocator:负责内存分配的 ByteBufAllocator。
byte[] array:缓冲区实现 byte 数组。
ByteBuffer tmpNioBuf:从 ByteBuf 到 NIO 的 ByteBuffer 的转换对象 tmpNioBuf

当buf没有引用使用时,清空缓存
release() -> protected void deallocate() {
array = null;
}

实现扩容和缩容
public ByteBuf capacity(int newCapacity) {
ensureAccessible();//判断是否被销毁
//是否无可读或可读超过最大缓冲容量
if (newCapacity < 0 || newCapacity > maxCapacity()) {
throw new IllegalArgumentException("newCapacity: " + newCapacity);
}

    int oldCapacity = array.length;//原capacity大小
    if (newCapacity > oldCapacity) {
        byte[] newArray = new byte[newCapacity];//按新的capacity大小创建新的缓冲数组
        System.arraycopy(array, 0, newArray, 0, array.length);//将数据复制到newArray中
        setArray(newArray);
    } else if (newCapacity < oldCapacity) {
        byte[] newArray = new byte[newCapacity];
        int readerIndex = readerIndex();
        if (readerIndex < newCapacity) {
            int writerIndex = writerIndex();
            if (writerIndex > newCapacity) {
                writerIndex(writerIndex = newCapacity);//设置指针位置,可写缓存为空
            }
            System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
        } else {
            setIndex(newCapacity, newCapacity);
        }
        setArray(newArray);//缩容
    }
    return this;
}

private void setArray(byte[] initialArray) {
    array = initialArray;//替换为新的数组缓存
    tmpNioBuf = null;//这时buf中尚未有数据,如果此时转为byteBuffer,
                     //则会造成数据错乱,因此在这里设置为null
}

public ByteBuf setIndex(int readerIndex, int writerIndex) {
    if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
        throw new IndexOutOfBoundsException(String.format(
                "readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
                readerIndex, writerIndex, capacity()));
    }
    this.readerIndex = readerIndex;
    this.writerIndex = writerIndex;
    return this;
}

设置指针位置
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
checkDstIndex(index, length, dstIndex, dst.length);
System.arraycopy(array, index, dst, dstIndex, length);
return this;
}

7.UnpooledDirectByteBuf 直接内存缓冲区管理
protected void deallocate() {
ByteBuffer buffer = this.buffer;
if (buffer == null) {
return;
}

    this.buffer = null;

    if (!doNotFree) {
        freeDirect(buffer);
    }
}

protected void freeDirect(ByteBuffer buffer) {
    PlatformDependent.freeDirectBuffer(buffer);
}    

7.PooledByteBuf内存池原理解析
netty的memory arena 是由多个chunk组成,而chunk又是由一个或多个page组成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值