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