1.成员变量
static final ResourceLeakDetector<ByteBuf> leakDetector = new ResourceLeakDetector<ByteBuf>(ByteBuf.class);
int readerIndex;
int writerIndex;
private int markedReaderIndex;
private int markedWriterIndex;
private int maxCapacity;
private SwappedByteBuf swappedBuf;
readerIndex,writeIndex,markedReaderIndex,markWriterIndex,maxCapacity这些都是很平常的变量。
而它的这两个属性,应该引起我们的注意:
一个是SwappedByteBuf swappedBuf;
这个是大端序列与小端序列的转换。
二个是ResourceLeakDetector
这个是Netty用来解决内存泄漏检测机制,非常重要。
由于AbstractByteBuf它继承了抽象类ByteBuf,所以它”不得不”把它的所有琐琐碎碎的函数给实现了,大部分都是一些返回信息的函数,这里就不再赘述,直接上难点!
2.读操作簇
取其中之一进行分析吧
@Override
public ByteBuf readBytes(ByteBuf dst, int dstIndex, int length) {
checkReadableBytes(length);
getBytes(readerIndex, dst, dstIndex, length);
readerIndex += length;
return this;
}
每个读操作都会先调用checkReadableBytes(length);
检查是否可读
protected final void checkReadableBytes(int minimumReadableBytes) {
ensureAccessible();
if (minimumReadableBytes < 0) {
throw new IllegalArgumentException("minimumReadableBytes: " + minimumReadableBytes + " (expected: >= 0)");
}
if (readerIndex > writerIndex - minimumReadableBytes) {
throw new IndexOutOfBoundsException(String.format(
"readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
readerIndex, minimumReadableBytes, writerIndex, this));
}
}
protected final void ensureAccessible() {
if (refCnt() == 0) {
throw new IllegalReferenceCountException(0);
}
}
这个函数非常简单,先检查计数器refcnt是否等于0,等于零说明被错误地引用,然后检查传进去的长度参数,小于零抛出错误,加上readIndex大于writeIndex也会抛出错误。
之后会调用getBytes(readerIndex, dst, dstIndex, length);
方法,点进去看一看
发现他是父类的抽象方法
public abstract ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length);
为什么没有实现这个方法呢,原因很简单,getBytes方法根据调用函数的对象的不同会采取不同策略,这个函数调用对象可以是一个内存中的ByteBuf也可以是一个堆中的ByteBuf,所以它就没实现罗。
3.写操作簇
有人问,读和写不是差不多嘛,其实不然,写操作有个不同之处,他可以动态扩展内存,是非常值得我们学习的地方。在随便找一个写操作的函数进行一番分析
@Override
public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) {
ensureAccessible();
ensureWritable(length);
setBytes(writerIndex, src, srcIndex, length);
writerIndex += length;
return this;
}
ensureAccessible()检查计数器是否为零, ensureWritable(length);检查是否可读,这个方法的厉害之处在于它可以动态扩展缓存区。
上代码!
@Override
public ByteBuf ensureWritable(int minWritableBytes) {
if (minWritableBytes < 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));
int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
// Adjust to the new capacity.
capacity(newCapacity);
return this;
}
1.如果长度参数小于零,抛出错误。
2.如果长度小于可读长度(capacity() - writerIndex
),老铁没毛病,过去吧。
3.如果长度大于最大可读长度(maxCapacity - writerIndex
),对不起,抛出错误。
4.最重要的情况大于可读长度小于最大可读长度
capacity() - writerIndex< n <maxCapacity - writerIndex
这时候需要动态扩展长度了,看回这段代码
int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
// Adjust to the new capacity.
capacity(newCapacity);
return this;
我们在他的父类ByteBuf中看到alloc()返回这个ByteBuf的分配者对象,所以先用分配者对象调用calculateNewCapacity
计算出一个适合的新的容量capacity。
然后调用 capacity(newCapacity)设置新的容量,点进去看一看!发现还是父类的方法
public abstract ByteBuf capacity(int newCapacity);
和上面的getBytes()方法一样没有实现,是不是和这个方法一样和ByteBuf是内存中的还是堆中的有关系呢?
没错,就是这样,capacity(newCapacity)进行扩容处理,当然会因为它的位置而采取不同的处理啊!