netty源碼分析11-ByteBuf

本文分享內容如下

  1. AbstractByteBuf分析
    1. readXXX系列方法
    2. 讀寫方法分析
    3. ensureWritable分析
    4. capacity分析
    5. setBytes(int, byte[], int, int) 分析
  2. AbstractReferenceCountedByteBuf分析
  3. ByteBuffer-put系列方法分析
  4. UnpooledHeapByteBuf分析
  5. Head,Direct,UnsafeDirect 三種類型ByteBuf區別分析
  6. 內存池分配器分析

ByteBuf概覽

主要類繼承圖如下

下面逐個進行分析

AbstractByteBuf分析

readXXX系列方法

//int 相當於4個byte大小,讀取4個byte位,拼接成int

public int readInt() {

checkReadableBytes(4);

int v = _getInt(readerIndex);//將4位byte按照高低拼接。

readerIndex += 4;

return v;

}

同理 readShort 2個byte,readLong 8個byte。

 

讀寫方法分析

AbstractByteBuf中讀寫方法比較多,下面讀寫各分析一個典型的方法

//將當前的ByteBuf中從dstIndex開始長度爲length的數據寫入到sdt中,讀取成功readerIndex增加length

public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {

checkReadableBytes(length);//簡單的校驗

getBytes(readerIndex, dst, dstIndex, length);//拷貝到src中

readerIndex += length;

return this;

}

 

將src中從srcIndex開始長度爲length的數據寫入到當前Buf中,寫入成功writerIndex增加length

public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {

ensureWritable(length);

setBytes(writerIndex, src, srcIndex, length);//與readBytes中使用同一方法

writerIndex += length;

return this;

}

ensureWritable(length);在讀寫中都用到了,下面詳細分析一下。

ensureWritable分析

public ByteBuf ensureWritable(int minWritableBytes) {

//各種check

// Normalize the current capacity to the power of 2.

//計算新容量

int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);

 

// Adjust to the new capacity.

capacity(newCapacity);//擴容或縮容量

return this;

}

 

private int calculateNewCapacity(int minNewCapacity) {

final int maxCapacity = this.maxCapacity;

final int threshold = 1048576 * 4; // 4 MiB page //大容量時 計劃每次增長4MB

 

if (minNewCapacity == threshold) {

return threshold;

}

 

// If over threshold, do not double but just increase by threshold.

if (minNewCapacity > threshold) {// 新容量較大,進行大容量擴容

int newCapacity = minNewCapacity / threshold * threshold; //獲取threshold整數倍的容量值

if (newCapacity > maxCapacity - threshold) {//應該增長threshold,但是最大容量不允許,新容量爲最大容量

newCapacity = maxCapacity;

} else {//增長threshold

newCapacity += threshold;

}

return newCapacity;

}

 

//容較小,進行小容量擴容

int newCapacity = 64;

//取64*2N次冪 大於minNewCapacity 的最小值

// 如 minNewCapacity=255 newCapacity 最後等於 512

while (newCapacity < minNewCapacity) {

newCapacity <<= 1;

}

//防止newCapacity超出maxCapacity,默認 maxCapacity是Integer.MAX, 如果newCapacity超出maxCapacity,newCapacity至少是maxCapacity的1/2,應該在大容量擴容就被處理掉了,此時下面這個取最小值沒有用。

return Math.min(newCapacity, maxCapacity);

}

 

capacity分析

capacity有不同的實現,下面舉例分析,其他實現邏輯上相同,細節有按照其特性實現。

io.netty.buffer.UnpooledDirectByteBuf-capacity分析

public ByteBuf capacity(int newCapacity) {

ensureAccessible();

if (newCapacity < 0 || newCapacity > maxCapacity()) {

throw new IllegalArgumentException("newCapacity: " + newCapacity);

}

 

int readerIndex = readerIndex();

int writerIndex = writerIndex();

 

int oldCapacity = capacity;

if (newCapacity > oldCapacity) {//擴容

ByteBuffer oldBuffer = buffer;

ByteBuffer newBuffer = allocateDirect(newCapacity);

oldBuffer.position(0).limit(oldBuffer.capacity());

newBuffer.position(0).limit(oldBuffer.capacity());

newBuffer.put(oldBuffer);//put的邏輯是將 src limit減去position 即src.remaining() 長度的byte拷貝到 newBuffer中,從position 開始。

//這裏是將oldBuffer中的byte拷貝到newBuffer中去,擴容的同時也複製了數據

newBuffer.clear();

setByteBuffer(newBuffer);

} else if (newCapacity < oldCapacity) {//縮容量

ByteBuffer oldBuffer = buffer;

ByteBuffer newBuffer = allocateDirect(newCapacity);

if (readerIndex < newCapacity) {//當前寫入索引小於新容量

if (writerIndex > newCapacity) { //readerIndex < newCapacity<writerIndex 只能截取 readerIndex ~newCapacity 的有效數據

writerIndex(writerIndex = newCapacity);

}

//當writerIndex < newCapacity==true,即readerIndex <writerIndex <newCapacity,不需要處理writerIndex

//拷貝oldBuffer有效數據到newBuffer

oldBuffer.position(readerIndex).limit(writerIndex);

newBuffer.position(readerIndex).limit(writerIndex);

newBuffer.put(oldBuffer);

newBuffer.clear();

} else {//這塊不理解,感覺有問題

setIndex(newCapacity, newCapacity);

}

setByteBuffer(newBuffer);//設置核心依賴的ByteBuffer

}

return this;

}

setBytes(int, byte[], int, int) 分析

setBytes與capacity情況類似,下面舉例分析。

UnpooledHeapByteBuf.setBytes(int, byte[], int, int) 分析

public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {

checkSrcIndex(index, length, srcIndex, src.length);//檢查 防止數組越界

System.arraycopy(src, srcIndex, array, index, length);//byte copy

return this;

}

使用arraycopy完成byte拷貝

到此 AbstractByteBuf的讀寫典型方法就分析完了

 

操作索引方法

這部分方法比較簡單,不詳細分析

public ByteBuf readerIndex(int readerIndex):設置讀索引

public ByteBuf writerIndex(int writerIndex):設置寫索引

還有mark,reset readerIndex和writerIndex方法。

@Override

public int maxWritableBytes() {

return maxCapacity() - writerIndex;

}

 

@Override

public ByteBuf markReaderIndex() {

markedReaderIndex = readerIndex;

return this;

}

 

@Override

public ByteBuf resetReaderIndex() {

readerIndex(markedReaderIndex);

return this;

}

 

@Override

public ByteBuf markWriterIndex() {

markedWriterIndex = writerIndex;

return this;

}

 

@Override

public ByteBuf resetWriterIndex() {

writerIndex = markedWriterIndex;

return this;

}

 

重用緩存方法分析

public ByteBuf discardReadBytes() {

ensureAccessible();

if (readerIndex == 0) {

return this;

}

 

if (readerIndex != writerIndex) {//數據被讀取部分

setBytes(0, this, readerIndex, writerIndex - readerIndex);//未讀數據前移

writerIndex -= readerIndex;

adjustMarkers(readerIndex);//調整mark值

readerIndex = 0;

} else {//數據全部讀取

adjustMarkers(readerIndex);

writerIndex = readerIndex = 0;

}

return this;

}

 

 

//跳過length個byte數據, readerIndex+=length

public ByteBuf skipBytes(int length) {

checkReadableBytes(length);

 

int newReaderIndex = readerIndex + length;

if (newReaderIndex > writerIndex) {//新的readerIndex不能超過writerIndex

throw new IndexOutOfBoundsException(String.format(

"length: %d (expected: readerIndex(%d) + length <= writerIndex(%d))",

length, readerIndex, writerIndex));

}

readerIndex = newReaderIndex;

return this;

}

 

AbstractReferenceCountedByteBuf分析

都是維護引用值的方法

引用值加1

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;

}

引用值加increment

public ByteBuf retain(int increment) {

//...

}

引用值減1

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;

}

}

}

引用值減decrement

public final boolean release(int decrement) {

//...

}

小結:AbstractReferenceCountedByteBuf中實現維護引用值得的多個方法

 

ByteBuffer-put系列方法分析

ByteBuf最終的實現類大量用到了ByteBuffer-put系列方法,瞭解其用法是很重要的。

注意:除了指定了寫入位置的方法,下面分析的方法都從當前ByteBuffe的position 開始,。

將 src的 limit減去position 即src.remaining() 長度的byte拷貝到 當前ByteBuffer 中。

public ByteBuffer put(ByteBuffer src);

 

將src從offset開始length長度的byte拷貝到 當前ByteBuffer 中。

public ByteBuffer put(byte[] src, int offset, int length)

 

寫入一個byte b值到 當前ByteBuffer 中。

public ByteBuffer put(byte b);

 

寫入一個char b值到 當前ByteBuffer 中,char 類型2個byte大小,value值被拆成高低2個byte。

public ByteBuffer putChar(char value);

由於int 4個byte,short 2個byte,long 8個byte, float4個byte,double,long 8個byte

還有public ByteBuffer putXXX(char value);方法與putChar方法同理

 

在指定的位置寫入value值

public abstract ByteBuffer putChar(int index, char value);

與putChar(char value)相同,還有 puttXXX(int index, char value);

 

UnpooledHeapByteBuf分析

getBytes系列方法

@Override

public ByteBuf getBytes(int index, ByteBuffer dst) {

ensureAccessible();

dst.put(array, index, Math.min(capacity() - index, dst.remaining()));

return this;

}

getBytes是將數據寫入指定目標,這個指定目標可以是 ByteBuf,byte[],ByteBuffer,OutputStream,GatheringByteChannel類型

UnpooledHeapByteBuf中只有一個readBytes方法,用於從當前buf中讀出指定長度寫到channel中去。

public int readBytes(GatheringByteChannel out, int length) throws IOException {

checkReadableBytes(length);

int readBytes = getBytes(readerIndex, out, length, true);

readerIndex += readBytes;

return readBytes;

}

setBytes系列方法

與getBytes相對應的是setBytes系列方法,是將指定目標數據寫入寫入當前buf,這個指定目標可以是 ByteBuf,byte[],ByteBuffer,OutputStream,GatheringByteChannel類型

獲取基本類型的拼接方法 如

protected int _getInt(int index) {

return (array[index] & 0xff) << 24 |

(array[index + 1] & 0xff) << 16 |

(array[index + 2] & 0xff) << 8 |

array[index + 3] & 0xff;

}

截取一段創建新的ByteBuf

public ByteBuf copy(int index, int length) {

checkIndex(index, length);

byte[] copiedArray = new byte[length];

System.arraycopy(array, index, copiedArray, 0, length);

return new UnpooledHeapByteBuf(alloc(), copiedArray, maxCapacity());

}

 

//將ByteBuf 轉換成ByteBuffer

private ByteBuffer internalNioBuffer() {

ByteBuffer tmpNioBuf = this.tmpNioBuf;

if (tmpNioBuf == null) {//use array new HeapByteBuffer

this.tmpNioBuf = tmpNioBuf = ByteBuffer.wrap(array);

}

return tmpNioBuf;

}

 

 

@Override

public ByteBuf copy(int index, int length) {

ensureAccessible();

ByteBuffer src;

try {

src = (ByteBuffer) buffer.duplicate().clear().position(index).limit(index + length);

} catch (IllegalArgumentException e) {

throw new IndexOutOfBoundsException("Too many bytes to read - Need " + (index + length));

}

 

return alloc().directBuffer(length, maxCapacity()).writeBytes(src);

}

ByteBuf pool, 創建工具類,輔助工具類。

 

Head,Direct,UnsafeDirect 三種類型ByteBuf區別分析

UnpooledHeadByteBuf

private UnpooledHeapByteBuf(

ByteBufAllocator alloc, byte[] initialArray, int readerIndex, int writerIndex, int maxCapacity) {

super(maxCapacity);

//。。。

this.alloc = alloc;

setArray(initialArray);//數組

setIndex(readerIndex, writerIndex);

}

//內部數據使用byte[]維護

 

存數據典型方法:setBytes(int, byte[], int, int)

數組實現

@Override

public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {

checkSrcIndex(index, length, srcIndex, src.length);

System.arraycopy(src, srcIndex, array, index, length);

return this;

}

UnpooledDirectByteBuf

構造方法

protected UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {

super(maxCapacity);

//...

this.alloc = alloc;

setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));//new DirectByteBuffer(capacity)

}

//與UnpooledUnsafeDirectByteBuf相同, 內部數據使用DirectByteBuffer維護。

 

存數據典型方法:setBytes(int, byte[], int, int)

NIO DirectByteBuffer 實現

public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {

checkSrcIndex(index, length, srcIndex, src.length);

ByteBuffer tmpBuf = internalNioBuffer();

tmpBuf.clear().position(index).limit(index + length);

tmpBuf.put(src, srcIndex, length);

return this;

}

UnpooledUnsafeDirectByteBuf

protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {

super(maxCapacity);

//...

this.alloc = alloc;

setByteBuffer(allocateDirect(initialCapacity));//new DirectByteBuffer(capacity)

}

//內部數據使用DirectByteBuffer維護。

存數據典型方法:setBytes(int, byte[], int, int)

使用Unsafe直接操作內存實現。

public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {

checkIndex(index, length);

if (length != 0) {

PlatformDependent.copyMemory(src, srcIndex, addr(index), length);

}

return this;

}

 

static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {

UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);

}

內存池分配器分析

常用的分配方法: AbstractByteBufAllocator.directBuffer(int, int)

public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {

if (initialCapacity == 0 && maxCapacity == 0) {

return emptyBuf;

}

validate(initialCapacity, maxCapacity);

return newDirectBuffer(initialCapacity, maxCapacity);

}

UnpooledByteBufAllocator的實現

//直接調用構造方法

protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {

ByteBuf buf;

if (PlatformDependent.hasUnsafe()) {

buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);

} else {

buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);

}

 

return toLeakAwareBuffer(buf);

}

PooledByteBufAllocator的實現

使用內存池

protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {

PoolThreadCache cache = threadCache.get();

PoolArena<ByteBuffer> directArena = cache.directArena;

 

ByteBuf buf;

if (directArena != null) {//通常會執行這裏

buf = directArena.allocate(cache, initialCapacity, maxCapacity);

} else {//執行 UnpooledByteBufAllocator的 newDirectBuffer邏輯

if (PlatformDependent.hasUnsafe()) {

buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);

} else {

buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);

}

}

 

return toLeakAwareBuffer(buf);

}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章