Buffer和相关辅助类
ByteBuffer
常用的缓冲区JDK NIO 类库 java.nio.Buffer
JDK提供的ByteBuffer可以满足NIO编程,但有其局限性:
ByteBuffer长度固定,不能自动扩缩容,编程对象POJO大于ByteBuffer的容量时,或发生索引越界异常ByteBuffer只有一个标识位置的指针position,读写的时候需要手工调用flip()和rewind()等,使用者必须必须小心谨慎地处理这些API,否则很容易导致程序处理失败;ByteBuffer的API功能有限,一些高级和实用的特性不支持,需要使用者自己编程实现
ByteBuf自动扩容iAbstractByteBuf#writeByte调用ensureWritable0方法
final void ensureWritable0(int minWritableBytes) {
ensureAccessible();
if (minWritableBytes <= writableBytes()) {
return;
}
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);
// Adjust to the new capacity.
capacity(newCapacity);
}
ByteBuf的工作原理
ButeBuf依然是个Byte数组的缓冲区,基本功能应该与JDK的ByteBuffer一致:
- 7种Java基本类型、byte数组、ByteBuffer(ByteBuf)等的读写
- 缓冲区自身的copy和slice等
- 设置网络字节序
- 构造缓冲区实例
- 操作位置指针等方法
Netty ByteBuf是根据ByteBuffer实现,现有两种策略:
- 参考
JDK ByteBuffer的实现,增加额外的功能,解决原ByteBuffer的缺点 - 聚合
JDK ByteBuffer,通过Facade模式对其进行包装,可以减少自身代码量,降低实现成本
ByteBuffer的读写

ByteBuffer不做flip操作,读取到将是
position到capacity之间的错误内容
ByteBuffer buffer = ByteBuffer.allocate(88);
String value = "Hello World";
buffer.put(value.getBytes());
buffer.flip();
byte[] vArray = new byte[buffer.remaining()];
buffer.get(vArray);
System.out.println(new String(vArray,"UTF-8"));
ByteBuf顺序读操作readxxx 类似于ByteBuffer的get操作顺序写操作writexxx 类似于ByteBuf的put操作随机读写(set和get)

readIndex和writerIndex
ByteBuf通过两个位置指针协助缓冲区读写操作,readIndex用于标识读取索引,调用ByteBuf的read操作,从readIndex处开始读writerIndex用于标识写入索引,调用ByteBuf的write操作,从writerIndex处开始写readIndex到writerIndex之间的空间为可读的字节缓冲区writerIndex·到capacity`之间为可写的字节缓冲区
Readable bytes和Writable byes
可读空间端(0-writerIndex)是数据实际存储的区域
调用read或skip开头会从readerIndex开始读取或跳过指定的数据,操作完成后,readerIndex增加了读取或者跳过的字节数长度。越界抛出IndexOutOfBoundsException
可写空间段(writerIndex-capacity)是尚未使用可以填充的空闲空间
调用write开头操作会从writerIndex向空闲空间写入字节,writerIndex随之增加,越界抛出IndexOutOfBoundsException
discardable
0到readIndex之间是已经读取过的缓冲区,视为discard
调用discardReadBytes操作,可以释放这部分空间,类似ByteBuffer#compact方法,可重用之间已经读取过的缓冲区
discardReadBytes会发生字节数组的内存复制,频繁调用将会导致性能下降
clear
ByteBuf 调用clear操作会重置readerIndex和writerIndex 类似JDK ByteBuffer的clear操作
Mark和Reset
markReaderIndex 将当前的readerIndex备份到markedReaderIndex中;resetReaderIndex 将当前的readerIndex设置为markedReaderIndex;markerWriterIndex 将当前的writerIndex备份到markedWriterIndex中resetWriterIndex 将当前的writerIndex设置为markedWriterIndex
ByteBuf主要类继承关系

从内存分配角度看,ByteBuf可以分类
- 堆内存(
HeapByteBuf): 优点内存分配和回收快,可以被JVM自动回收;缺点:如果进行Socket的I/O读写,需要额外做一次内存复制,将堆内存对应的缓冲区复制到内核Channel中性能会有有一定程度的下降 - 直接内存(
DirectByteBuf)字节缓冲区: 非堆内存,它在堆外进行内存分配,相比与堆内存,它的分配和回收速度会慢一些,但SocketChannel的读写少一次内存复制
ByteBuf最佳实践在I/O通信线程读写缓冲区使用DirectByteBuf,后端业务编码模块使用HeadByteBuf
从内存回收角度分类:pool基于对象池的ByteBuf和普通ByteBuf,对象池的管理和维护复杂
AbstractByteBuf
读写索引,mark,最大容量等公共属性方法定义
// 所有ByteBuf实例共享 ResourceLeakDetector用于检测对象是否泄漏
static final ResourceLeakDetector<ByteBuf> leakDetector =
ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class);
int readerIndex;
int writerIndex;
private int markedReaderIndex;
private int markedWriterIndex;
private int maxCapacity;
AbstractReferenceCountedByteBuf
对引用进行计数,类似于JVM内存回收对象引用计数器,用于跟踪对象的分配和销毁,做自动内存回收
...
// 通过原子方式对成员变量进行更新操作,线程安全,消除锁
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater;
static {
AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> updater =
PlatformDependent.newAtomicIntegerFieldUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
if (updater == null) {
updater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
}
refCntUpdater = updater;
}
// 跟踪对象的引用次数 volatile 解决多线程的可见性
private volatile int refCnt = 1;
...
// 自旋 引用计算加一 cas操作
@Override
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;
}
// 自旋 释放引用计数
@Override
public final boolean release() {
for (;;) {
int refCnt = this.refCnt;
if (refCnt == 0) {
throw new IllegalReferenceCountException(0, -1);
}
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
if (refCnt == 1) {
deallocate();
return true;
}
return false;
}
}
}
UnpooledHeapByteBuf
基于堆内存分配字节缓冲区,没有基于对象池技术实现
``java
public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
//
private final ByteBufAllocator alloc;
private byte[] array;
private ByteBuffer tmpNioBuf;
来源:CSDN
作者:向着风奔跑
链接:https://blog.csdn.net/liulong1010/article/details/104181280