当前位置 : 主页 > 手机开发 > 其它 >

ByteBuf源码

来源:互联网 收集:自由互联 发布时间:2021-06-19
ByteBuf是顶层的抽象类,定义了用于传输数据的ByteBuf需要的方法和属性。 AbstractByteBuf 直接继承ByteBuf,一些公共属性和方法的公共逻辑会在这里定义。例如虽然不同性质的ByteBuf底层实现

  ByteBuf是顶层的抽象类,定义了用于传输数据的ByteBuf需要的方法和属性。

AbstractByteBuf

  直接继承ByteBuf,一些公共属性和方法的公共逻辑会在这里定义。例如虽然不同性质的ByteBuf底层实现不同如堆Buffer和直接Buffer,但在进行数据写入的时候都要检查并移动readIndex,因此诸如检查并移动readIndex的方法就抽取并定义在AbstractByteBuf方法中。

 

公共成员变量

  分为两大类,一是读写索引和mark索引,一个是leakDetector,用于检查是否存在内存泄露,该成员变量是static意味着所有ByteBuf的实例共享同一个leakDetector,单例设计模式处处可见。

static final ResourceLeakDetector<ByteBuf> leakDetector;
int readerIndex;
int writerIndex;
private int markedReaderIndex;
private int markedWriterIndex;
private int maxCapacity;

 

读操作簇

  以readBytes为例,在AbstractByteBuf里主要做了两件事情

  • 合法性检查
  • 调用getBytes,之所以用调用是因为getBytes会因为子类的不同实现而有所区别
  • 索引移动
    public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
        this.checkReadableBytes(length);
        this.getBytes(this.readerIndex, dst, dstIndex, length);
        this.readerIndex += length;
        return this;
    }

   

写操作簇

  同样做了三件事情

  • 合法性检查
  • 调用setBytes
  • 索引移动  
    public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) {
        this.ensureWritable(length);
        this.setBytes(this.writerIndex, src, srcIndex, length);
        this.writerIndex += length;
        return this;
    }

  在ensureWritable中封装了扩容逻辑,这也是ByteBuf比NIO中的ByteBuffer优秀的地方。首先检查传入的length是否小于零,小于零则抛出异常,否则执行ensureWritable0,Netty给方法起的名字总是这么随意……

    public ByteBuf ensureWritable(int minWritableBytes) {
        if (minWritableBytes < 0) {
            throw new IllegalArgumentException(String.format("minWritableBytes: %d (expected: >= 0)", minWritableBytes));
        } else {
            this.ensureWritable0(minWritableBytes);
            return this;
        }
    }
  •  如果当前期望写入的数据长度(minWritableByte)大于此时ByteBuf的长度(writableBytes)且大于剩余的最大容量(this.MaxCapacity-this.writerIndex),抛出异常。ByteBuf真的不能写了。

  •  如果当前期望写入的数据长度(minWritableByte)大于此时ByteBuf的长度(writableBytes),但小于剩余的最大容量(this.MaxCapacity-this.writerIndex),调用calculateNewCapacity扩容,返回扩容后的大小(newCapacity),并重新设置当前ByteBuf(this.capacity(newCapacity))。
  •  如果当前期望写入的数据长度(minWritableByte)小于此时ByteBuf的长度,啥也不干。
final void ensureWritable0(int minWritableBytes) {
        this.ensureAccessible();
        if (minWritableBytes > this.writableBytes()) {
            if (minWritableBytes > this.maxCapacity - this.writerIndex) {
                throw new IndexOutOfBoundsException(String.format("writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s", this.writerIndex, minWritableBytes, this.maxCapacity, this));
            } else {
                int newCapacity = this.alloc().calculateNewCapacity(this.writerIndex + minWritableBytes, this.maxCapacity);
                this.capacity(newCapacity);
            }
        }
    }

 

  calculateNewCapacity传入两个参数:能够完成这里写入所需要的最小容量(minNewCapacity),当前支持的最大容量(maxCapacity)。

  • 首先合法性检查,minNewCapacity<0或者minNewCapacity>maxCapacity抛出异常。
  • 如果需要的容量等于4MB(minNewCapacity==4194304),那么把4MB作为扩容后的容量并返回。
  • 如果需要的容量大于4MB,首先找到一个小于等于4MB整数倍的容量(newCapacity),如果newCapacity加上4MB大于maxCapacity则返回maxCapacity,否则返回newCapacity加4MB,保证扩容后的容量永远是4MB的整数倍,这种思想和HashMap扩容很接近,有人把这种扩容称为步进4MB扩容方式。
  • 如果需要的容量小于4MB,从64开始倍增,直到大于等于需要的容量。
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
        if (minNewCapacity < 0) {
            throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expected: 0+)");
        } else if (minNewCapacity > maxCapacity) {
            throw new IllegalArgumentException(String.format("minNewCapacity: %d (expected: not greater than maxCapacity(%d)", minNewCapacity, maxCapacity));
        } else {
            int threshold = 4194304;
            if (minNewCapacity == 4194304) {
                return 4194304;
            } else {
                int newCapacity;
                if (minNewCapacity > 4194304) {
                    newCapacity = minNewCapacity / 4194304 * 4194304;
                    if (newCapacity > maxCapacity - 4194304) {
                        newCapacity = maxCapacity;
                    } else {
                        newCapacity += 4194304;
                    }

                    return newCapacity;
                } else {
                    for(newCapacity = 64; newCapacity < minNewCapacity; newCapacity <<= 1) {
                    }

                    return Math.min(newCapacity, maxCapacity);
                }
            }
        }
    }

 

  这里应该还有一个复制的操作,但是在这个抽象类里没有找到,应该在具体的实现类中。

重用缓冲区

  把已读的部分[0,readIndex]重用起来。

  • 如果readIndex==0,没有可以重用的区域。
  • 如果readIndex!=0且readIndex!=writeIndex,说明在0~readIndex区间内的内存可以重用,调用setBytes,把尚未读取的内容复制到缓冲区的开始,然后把writerIndex向前挪(this.writerIndex-=this.readerIndex),重设读索引为0。
  • 如果readIndex!=0且readIndex==writeIndex,说明有可以重用的内存,且不存在还没有读取的数据,直接重设读写索引为0(readIndex=writeIndex=0)
    public ByteBuf discardReadBytes() {
        this.ensureAccessible();
        if (this.readerIndex == 0) {
            return this;
        } else {
            if (this.readerIndex != this.writerIndex) {
                this.setBytes(0, this, this.readerIndex, this.writerIndex - this.readerIndex);
                this.writerIndex -= this.readerIndex;
                this.adjustMarkers(this.readerIndex);
                this.readerIndex = 0;
            } else {
                this.adjustMarkers(this.readerIndex);
                this.writerIndex = this.readerIndex = 0;
            }

            return this;
        }
    }
网友评论