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组成