ByteBuf是netty中數(shù)據(jù)傳輸?shù)娜萜鞣郑脕硖娲鶱IO中的ByteBuffer孩哑。其主要還是一個byte數(shù)組,以及包含了一些數(shù)據(jù)的操作方法翠桦。通過兩個指針readerIndex和writerIndex來讀寫分離横蜒,更好的處理數(shù)據(jù)。其結(jié)構(gòu)大致如下圖所示销凑。
而從內(nèi)存分配的角度來講愁铺,ByteBuf又分為兩種,DirectByteBuf和HeapByteBuf闻鉴。簡而言之就是一種是分配在Direct Memory上的,一種是分配在Heap Memory上的茂洒。
這里稍微解釋一下direct memory孟岛。直接內(nèi)存(Direct Memory)并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域,但是這部分內(nèi)存也被頻繁地使用渠羞,而且也可能導(dǎo)致異常出現(xiàn)斤贰。它是在JDK 1.4 中新加入了NIO(New Input/Output)類,引入了一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O 方式次询,它可以使用Native 函數(shù)庫直接分配堆外內(nèi)存荧恍,然通過一個存儲在Java 堆里面的DirectByteBuffer 對象作為這塊內(nèi)存的引用進行操作。這樣能在一些場景中顯著提高性能屯吊,因為避免了在Java 堆和Native 堆中來回復(fù)制數(shù)據(jù)送巡。
直接內(nèi)存的好處就是利用的是native庫,讀寫快速盒卸。但是它不在虛擬機的管理范圍之內(nèi)骗爆,這部分內(nèi)存只有在進行full gc時才會進行回收,而他的容量如果沒有明確限制蔽介,隨著數(shù)據(jù)的不斷讀寫勢必造成內(nèi)存中可利用的空間不斷變小摘投。所以netty做了引用計數(shù)機制來處理direct memory上的數(shù)據(jù)。其實對heap上的也做了引用計數(shù)虹蓄。
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater;
private volatile int refCnt = 1;
其主要用了這兩個變量來處理引用計數(shù)問題犀呼。計數(shù)器基于 AtomicIntegerFieldUpdater,為什么不直接用AtomicInteger薇组?因為ByteBuf對象很多外臂,如果都把int包一層AtomicInteger花銷較大,而AtomicIntegerFieldUpdater只需要一個全局的靜態(tài)變量体箕。
retain
@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;
}
release
@Override
public 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;
}
}
}
這兩種ByteBuf有各自的特點专钉,用于針對不同的場景。
HeapByteBuf :特點是內(nèi)存的分配和回收速度快累铅≡拘耄可以被jvm自動回收。缺點就是在進行socket的I/O讀寫時娃兽,需要將堆內(nèi)存的緩沖區(qū)拷貝到內(nèi)核中菇民,有一定拷貝的代價。
DirectByteBuf:特點是分配在堆內(nèi)存外投储,相比于堆內(nèi)存分配速度會慢一點第练,并需要手動管理其引用計數(shù),清理不使用的內(nèi)存玛荞。但是其直接用native方法娇掏,不需要拷貝。
ByteBuf從內(nèi)存回收策略上來說也分為兩種pool和unpool勋眯。其中poolByteBuf主要是建立了一塊內(nèi)存池來管理ByteBuf婴梧。其主要為一塊poolArena下梢,其中維護這多個poolChunk。其變量大致如下塞蹭。
bstract class PoolArena<T> implements PoolArenaMetric {
static final boolean HAS_UNSAFE = PlatformDependent.hasUnsafe();
enum SizeClass {
Tiny,
Small,
Normal
}
static final int numTinySubpagePools = 512 >>> 4;
final PooledByteBufAllocator parent;
private final int maxOrder;
final int pageSize;
final int pageShifts;
final int chunkSize;
final int subpageOverflowMask;
final int numSmallSubpagePools;
private final PoolSubpage<T>[] tinySubpagePools;
private final PoolSubpage<T>[] smallSubpagePools;
private final PoolChunkList<T> q050;
private final PoolChunkList<T> q025;
private final PoolChunkList<T> q000;
private final PoolChunkList<T> qInit;
private final PoolChunkList<T> q075;
private final PoolChunkList<T> q100;
private final List<PoolChunkListMetric> chunkListMetrics;