ByteBuf是netty中數(shù)據(jù)傳輸?shù)娜萜鳎脕硖娲鶱IO中的ByteBuffer。其主要還是一個byte數(shù)組,以及包含了一些數(shù)據(jù)的操作方法。通過兩個指針readerIndex和writerIndex來讀寫分離,更好的處理數(shù)據(jù)。其結(jié)構(gòu)大致如下圖所示。
ByteBuf示意圖而從內(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;
新聞熱點
疑難解答