1. 程式人生 > >ByteBuffer和ByteBuf原始碼解析

ByteBuffer和ByteBuf原始碼解析

學下下網路程式設計中常用的兩個Buffer,ByteBuffer和ByteBuf,接下來會分析兩者的細節及總結。

一.ByteBuffer

  ByteBuffer是JDK NIO中提供的java.nio.Buffer, 在記憶體中預留指定大小的儲存空間來存放臨時資料,其他Buffer 的子類有:CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer

 1. Buffer

   ByteBuffer繼承Buffer,Buffer中定義的成員變數。

 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class Buffer {

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

    // Used only by direct buffers
    // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
    long address;

每個Buffer都有以下的屬性:

capacity
這個Buffer最多能放多少資料。capacity在buffer被建立的時候指定。

limit
在Buffer上進行的讀寫操作都不能越過這個下標。當寫資料到buffer中時,limit一般和capacity相等,當讀資料時,
limit代表buffer中有效資料的長度。

position
讀/寫操作的當前下標。當使用buffer的相對位置進行讀/寫操作時,讀/寫會從這個下標進行,並在操作完成後,
buffer會更新下標的值。

mark
一個臨時存放的位置下標。呼叫mark()會將mark設為當前的position的值,以後呼叫reset()會將position屬性設
置為mark的值。mark的值總是小於等於position的值,如果將position的值設的比mark小,當前的mark值會被拋棄掉。

這些屬性總是滿足以下條件:
0 <= mark <= position <= limit <= capacity

走個ByteBuffer的小例子
import java.nio.ByteBuffer;

public class ByteBufferTest {

	public static void main(String[] args) {
	
		//例項初始化
		ByteBuffer buffer = ByteBuffer.allocate(100);
        String value ="Netty";
        buffer.put(value.getBytes());
        buffer.flip();
        byte[] vArray = new byte[buffer.remaining()];
        buffer.get(vArray);
        System.out.println(new String(vArray));
	}

}
我們看下呼叫flip()操作前後的對比

+--------------------+-----------------------------------------------------------+

|        Netty          |                                                                           |

+--------------------+-----------------------------------------------------------+

|                          |                                                                           |

0                       position                                                        limit = capacity 

                                      ByteBuffer flip()操作之前

+--------------------+-----------------------------------------------------------+

|        Netty          |                                                                           |

+--------------------+-----------------------------------------------------------+

|                          |                                                                           |

position              limit                                                                 capacity

                                      ByteBuffer flip()操作之後

由於ByteBuffer只有一個position位置指標使用者處理讀寫請求操作,因此每次讀寫的時候都需要呼叫flip()和clean()等方法,否則功能將出錯。如上圖,如果不做flip操作,讀取到的將是position到capacity之間的錯誤內容。當執行flip()操作之後,它的limit被設定為position,position設定為0,capacity不變,由於讀取的內容是從position都limit之間,因此它能夠正確的讀取到之前寫入緩衝區的內容。

3.Buffer常用的函式

 clear()
把position設為0,把limit設為capacity,一般在把資料寫入Buffer前呼叫。

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

 flip()
把limit設為當前position,把position設為0,一般在從Buffer讀出資料前呼叫。

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

rewind()

把position設為0,limit不變,一般在把資料重寫入Buffer前呼叫。

 public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

mark()

設定mark的值,mark=position,做個標記。

reset()

還原標記,把mark的值賦值給position。

4.ByteBuffer例項化

allocate(int capacity)

從堆空間中分配一個容量大小為capacity的byte陣列作為緩衝區的byte資料儲存器,實現類是HeapByteBuffer 。

 public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

allocateDirect(int capacity)
非JVM堆疊而是通過作業系統來建立記憶體塊用作緩衝區,它與當前作業系統能夠更好的耦合,因此能進一步提高I/O操作速度。但是分配直接緩衝區的系統開銷很大,因此只有在緩衝區較大並長期存在,或者需要經常重用時,才使用這種緩衝區,實現類是DirectByteBuffer。

 public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

wrap(byte[] array)

這個緩衝區的資料會存放在byte陣列中,bytes陣列或buff緩衝區任何一方中資料的改動都會影響另一方。其實ByteBuffer底層本來就有一個bytes陣列負責來儲存buffer緩衝區中的資料,通過allocate方法系統會幫你構造一個byte陣列,實現類是HeapByteBuffer 。

wrap(byte[] array,int offset, int length)

在上一個方法的基礎上可以指定偏移量和長度,這個offset也就是包裝後byteBuffer的position,而length呢就是limit-position的大小,從而我們可以得到limit的位置為length+position(offset),實現類是HeapByteBuffer 。

HeapByteBuffer和DirectByteBuffer的總結:前者是記憶體的分派和回收速度快,可以被JVM自動回收,缺點是如果進行Socket的I/O讀寫,需要額外做一次記憶體拷貝,將堆記憶體對應的快取區複製到核心中,效能會有一定程式的下降;後者非堆記憶體,它在堆外進行記憶體的分配,相比堆記憶體,它的分配和回收速度會慢一些,但是它寫入或者從Socket Channel中讀取時,由於少了一次記憶體複製,速度比堆記憶體快。經驗表明,最佳實踐是在I/O通訊執行緒的讀寫緩衝區使用DirectByteBuffer,後端業務訊息的編碼模組使用HeapByteBuffer,這樣的組合可以達到效能最優。

二. ByteBuf

先走個小例子

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;


public class ByteBufTest {

	public static void main(String[] args) {
	
		//例項初始化
		ByteBuf buffer =   Unpooled.buffer(100);
        String value ="學習ByteBuf";
        buffer.writeBytes(value.getBytes());
        System.out.println("獲取readerIndex:"+buffer.readerIndex());
        System.out.println("獲取writerIndex:"+buffer.writerIndex());
        byte[] vArray = new byte[buffer.writerIndex()];
        buffer.readBytes(vArray);
        System.out.println("獲取readerIndex:"+buffer.readerIndex());
        System.out.println("獲取writerIndex:"+buffer.writerIndex());
        System.out.println(new String(vArray));
        
        
	}

}

接著看下ByteBuf主要類繼承關係


1. AbstractByteBuf

AbstractByteBuf繼承ByteBuf,AbstractByteBuf中定義了ByteBuf的一些公共屬性,像讀索引、寫索引、mark、最大容量等公共屬性,具體定義如下圖。
public abstract class AbstractByteBuf extends ByteBuf {

    static final ResourceLeakDetector<ByteBuf> leakDetector = new ResourceLeakDetector<ByteBuf>(ByteBuf.class);
    
    int readerIndex;  //讀索引
    private int writerIndex; //寫索引

    private int markedReaderIndex; //,
    private int markedWriterIndex;

    private int maxCapacity;

    private SwappedByteBuf swappedBuf;
在AbstractByteBuf中並沒有定義ByteBuf的緩衝區實現,因為AbstractByteBuf並不清楚子類到底是基於堆記憶體還是直接記憶體。AbstractByteBuf中定義了讀寫操作方法,這裡主要介紹下寫方法,ByteBuf寫操作支援自動擴容,ByteBuffer而不支援,我們看下writeByte()具體的原始碼。
  @Override
    public ByteBuf writeByte(int value) {
        ensureWritable(1);
        setByte(writerIndex++, value);
        return this;
    }

接著呼叫ensureWritable()方法,是否需要自動擴容。

 @Override
    public ByteBuf ensureWritable(int minWritableBytes) {
        if (minWritableBytes < 0) {
            throw new IllegalArgumentException(String.format(
                    "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
        }

        if (minWritableBytes <= writableBytes()) { //writableBytes()計算可寫的容量=“capacity() - writerIndex;”

            return this;
        }

        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 = calculateNewCapacity(writerIndex + minWritableBytes);

        // Adjust to the new capacity.
        capacity(newCapacity);
        return this;
    }
接著繼續呼叫calculateNewCapacity(),計算自動擴容後容量,即滿足要求的最小容量,等於writeIndex+minWritableBytes。
private int calculateNewCapacity(int minNewCapacity) {
        final int maxCapacity = this.maxCapacity;
        final int threshold = 1048576 * 4; // 4 MiB page

        if (minNewCapacity == threshold) {
            return threshold;
        }

        // If over threshold, do not double but just increase by threshold.
        if (minNewCapacity > threshold) {
            int newCapacity = minNewCapacity / threshold * threshold;
            if (newCapacity > maxCapacity - threshold) {
                newCapacity = maxCapacity;
            } else {
                newCapacity += threshold;
            }
            return newCapacity;
        }

        // Not over threshold. Double up to 4 MiB, starting from 64.
        int newCapacity = 64;
        while (newCapacity < minNewCapacity) {
            newCapacity <<= 1;
        }

        return Math.min(newCapacity, maxCapacity);
    }
首先設定門限值為4MB,當需要的新容量正好等於門限值時,使用門限值作為新的快取區容量,如果新申請的記憶體容量大於門限值,不能採用倍增的方式擴張內容(防止記憶體膨脹和浪費),而是採用每次進步4MB的方式來記憶體擴張,擴張的時候需要對擴張後的記憶體和最大記憶體進行對比,如果大於快取區的最大長度,則使用maxCapacity作為擴容後的快取區容量。如果擴容後的新容量小於門限值,則以64為計算進行倍增,知道倍增後的結果大於等於需要的值。

重用快取區,重用已經讀取過的快取區,下面介紹下discardReadBytes()方法的實現進行分析

 @Override
    public ByteBuf discardReadBytes() {
        ensureAccessible();
        if (readerIndex == 0) {
            return this;
        }

        if (readerIndex != writerIndex) {
            //複製陣列 System.arraycopy(this,readerIndex, ,array,0,writerIndex - readerIndex)
            setBytes(0, this, readerIndex, writerIndex - readerIndex);
            writerIndex -= readerIndex;
            adjustMarkers(readerIndex);
            readerIndex = 0;
        } else {
            adjustMarkers(readerIndex);
            writerIndex = readerIndex = 0;
        }
        return this;
    }

首先對度索引進行判斷,如果為0則說明沒有可重用的快取區,直接返回,如果讀索引大於0且讀索引不等於寫索引,說明緩衝區中既有已經讀取過的被丟棄的緩衝區,也有尚未讀取的可讀取快取區。呼叫setBytes(0, this, readerIndex, writerIndex - readerIndex)進行位元組陣列複製,將尚未讀取的位元組陣列複製到緩衝區的起始位置,然後重新設定讀寫索引,讀索引為0,寫索引設定為之前的寫索引減去讀索引。在設定讀寫索引的同時,調整markedReaderIndex和markedWriterIndex。

接下來看下初始化分配的ByteBuf的結構圖

 *
 *      +-------------------+------------------+------------------+
 *      |                       writable bytes  
 *      +-------------------+------------------+------------------+
 *      |                                                         |
 *      0=readerIndex=writerIndex                             capacity
 *
ByteBuf通過兩個位置指標來協助緩衝區的讀寫操作,讀操作使用readerIndex,寫操作使用writerIndex。readerIndex和writerIndex的取值一開始都是0,隨著資料的寫入writerIndex會增加,讀取資料會readerIndex增加,但是它不會超出writerIndex。在讀取之後,0~readerIndex就視為discard的,呼叫discardReadBytes()方法,可以釋放這部分空間。readerIndex和writerIndex之間的資料是可讀的,等價於ByteBuffer position和limit之間的資料。writerIndex和capacity之間的空間是可寫的,等價於ByteBuffer  limit和capacity之間的可用空間。

寫入N個位元組後的ByteBuf

 *
 *      +-------------------+------------------+------------------+
 *      |         readable bytes               |  writable bytes  |
 *      +-------------------+------------------+------------------+
 *      |                                                         |
 *      0=readerIndex                      writerIndex        capacity
 *
讀取M(<N)個位元組之後的ByteBuf
 *
 *      +-------------------+------------------+------------------+
 *      | discardable bytes |  readable bytes  |  writable bytes  |
 *      +-------------------+------------------+------------------+
 *      |                   |                  |                  |
 *      0             M=readerIndex      N=writerIndex         capacity
 *

呼叫discardReadBytes操作之後的ByetBuf
 *
 *      +-------------------+------------------+------------------+
 *      |  readable bytes   |            writable bytes  
 *      +-------------------+------------------+------------------+
 *      |                                                         |
 *      0=readerIndex     N-M=writerIndex                      capacity
 *
呼叫clear操作之後的ByteBuf
 *
 *      +-------------------+------------------+------------------+
 *      |                       writable bytes(more space) 
 *      +-------------------+------------------+------------------+
 *      |                                                         |
 *      0=readerIndex=writerIndex                             capacity
 *

2.AbstractReferenceCountedByteBuf

  AbstractReferenceCountedByteBuf繼承AbstractByteBuf,從類的名字可以看出該類是對引用進行計數,用於跟蹤物件的分配和銷燬,做自動記憶體回收。

public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {

    private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
            AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");

    private static final long REFCNT_FIELD_OFFSET;

    static {
        long refCntFieldOffset = -1;
        try {
            if (PlatformDependent.hasUnsafe()) {
                refCntFieldOffset = PlatformDependent.objectFieldOffset(
                        AbstractReferenceCountedByteBuf.class.getDeclaredField("refCnt"));
            }
        } catch (Throwable t) {
            // Ignored
        }

        REFCNT_FIELD_OFFSET = refCntFieldOffset;
    }

    @SuppressWarnings("FieldMayBeFinal")
    private volatile int refCnt = 1;
首先看到第一個欄位refCntUpdater ,它是AtomicIntegerFieldUpdater型別變數,通過原子方式對成員變數進行更新等操作,以實現執行緒安全,消除鎖。第二個欄位是REFCNT_FIELD_OFFSET,它用於標識refCnt欄位在AbstractReferenceCountedByteBuf 中記憶體地址,該地址的獲取是JDK實現強相關的,如果是SUN的JDK,它通過sun.misc.Unsafe的objectFieldOffset介面獲得的,ByteBuf的實現類UnpooledUnsafeDirectByteBuf和PooledUnsafeDirectByteBuf會使用這個偏移量。最後定義一個volatile修飾的refCnt欄位用於跟蹤物件的引用次數,使用volatile是為了解決多執行緒併發的可見性問題。

物件引用計數器,每次呼叫一次retain,引用計數器就會加一,由於可能存在多執行緒併發呼叫的場景,所以他的累計操作必須是執行緒安全的,看下具體的實現細節。

  @Override
    public ByteBuf retain(int increment) {
        if (increment <= 0) {
            throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)");
        }

        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, increment);
            }
            if (refCnt > Integer.MAX_VALUE - increment) {
                throw new IllegalReferenceCountException(refCnt, increment);
            }
            if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) {
                break;
            }
        }
        return this;
    }

通過自旋對引用計數器進行加一操作,由於引用計數器的初始值為1,如果申請和釋放操作能保證正確使用,則它的最小值為1。當被釋放和被申請的次數相等時,就呼叫回收方法回收當前的ByteBuf物件。通過compareAndSet進行原子更新,它會使用自己獲取的值和期望值進行對比,一樣則修改,否則進行自旋,繼續嘗試直到成功(compareAndSet是作業系統層面提供的原子操作,稱為CAS)。釋放引用計數器的程式碼和物件引用計數器類似,釋放引用計數器的每次減一,當refCnt==1時意味著申請和釋放相等,說明物件引用已經不可達,該物件需要被釋放和回收。回收則是通過呼叫子類的deallocate方法來釋放ByteBuf物件。

看下UnpooledHeapByteBuf中deallocate的實現

 @Override
    protected void deallocate() {
        array = null;
    }
看下UnpooledUnsafeDirectByteBuf和UnpooledDirectByteBuf的deallocate實現細節
 @Override
    protected void deallocate() {
        ByteBuffer buffer = this.buffer;
        if (buffer == null) {
            return;
        }

        this.buffer = null;

        if (!doNotFree) {
            freeDirect(buffer);
        }
    }
再看freeDirect
protected void freeDirect(ByteBuffer buffer) {
        PlatformDependent.freeDirectBuffer(buffer);
    }
再看freeDirectBuffer
/**
     * Try to deallocate the specified direct {@link ByteBuffer}.  Please note this method does nothing if
     * the current platform does not support this operation or the specified buffer is not a direct buffer.
     */
    public static void freeDirectBuffer(ByteBuffer buffer) {
        if (buffer.isDirect()) {
            if (hasUnsafe()) {
                PlatformDependent0.freeDirectBufferUnsafe(buffer);
            } else {
                PlatformDependent0.freeDirectBuffer(buffer);
            }
        }
    }

PlatformDependent0.freeDirectBufferUnsafe(buffer)實現細節

static void freeDirectBufferUnsafe(ByteBuffer buffer) {
        Cleaner cleaner;
        try {
            cleaner = (Cleaner) getObject(buffer, CLEANER_FIELD_OFFSET);
            if (cleaner == null) {
                throw new IllegalArgumentException(
                        "attempted to deallocate the buffer which was allocated via JNIEnv->NewDirectByteBuffer()");
            }
            cleaner.clean();
        } catch (Throwable t) {
            // Nothing we can do here.
        }
    }
PlatformDependent0.freeDirectBuffer(buffer)實現細節
 static void freeDirectBuffer(ByteBuffer buffer) {
        if (CLEANER_FIELD == null) {
            return;
        }
        try {
            Cleaner cleaner = (Cleaner) CLEANER_FIELD.get(buffer);
            if (cleaner == null) {
                throw new IllegalArgumentException(
                        "attempted to deallocate the buffer which was allocated via JNIEnv->NewDirectByteBuffer()");
            }
            cleaner.clean();
        } catch (Throwable t) {
            // Nothing we can do here.
        }
    }
可以看到UnpooledUnsafeDirectByteBuf和UnpooledDirectByteBuf的deallocate最終都是通過Cleaner類進行堆外的垃圾回收。Cleaner 是PhantomReference(虛引用)的子類。

3. UnpooledHeapByteBuf

 UnpooledHeapByteBuf是AbstractReferenceCountedByteBuf的子類,UnpooledHeapByteBuf是基於堆記憶體進行記憶體分配的位元組碼快取區,它沒有基於物件池技術實現,這就意味著每次I/O的讀寫都會建立一個新的UnpooledHeapByteBuf,頻繁進行大塊記憶體的分配和回收對效能造成一定的影響,但是相比堆外記憶體的申請和釋放,它的成本還是會低一些。

看下UnpooledHeapByteBuf的成員變數定義

public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {

    private final ByteBufAllocator alloc;
    private byte[] array;
    private ByteBuffer tmpNioBuf;
首先它聚合了一個ByteBufAllocator,用於UnpooledHeapByteBuf的記憶體分配,緊接著定義了一個byte陣列作為緩衝區,最後定義一個ByteBuffer型別的tmpNioBuf變數用於實現Netty ByteBuf到JDK NIO ByteBuffer的轉正。

看下UnpooledHeapByteBuf類緩衝區的自動擴充套件的實現

@Override
    public ByteBuf capacity(int newCapacity) {
        ensureAccessible();
        if (newCapacity < 0 || newCapacity > maxCapacity()) {
            throw new IllegalArgumentException("newCapacity: " + newCapacity);
        }

        int oldCapacity = array.length;
        if (newCapacity > oldCapacity) {
            byte[] newArray = new byte[newCapacity];
            System.arraycopy(array, 0, newArray, 0, array.length);
            setArray(newArray);
        } else if (newCapacity < oldCapacity) {
            byte[] newArray = new byte[newCapacity];
            int readerIndex = readerIndex();
            if (readerIndex < newCapacity) {
                int writerIndex = writerIndex();
                if (writerIndex > newCapacity) {
                    writerIndex(writerIndex = newCapacity);
                }
                System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
            } else {
                setIndex(newCapacity, newCapacity);
            }
            setArray(newArray);
        }
        return this;
    }
方法入口首先對新容量進行合法性校驗,不通過則丟擲IllegalArgumentException,然後判斷新的容量是否大於當前的緩衝區容量,如果大於容量則進行動態擴容,通過new byte[newCapacity]建立新的緩衝區位元組陣列,然後通過System.arraycopy()進行記憶體複製,將舊的位元組陣列複製到新建立的位元組陣列中,最後呼叫setArray替代舊的位元組陣列。
如果新的容量小於當前的緩衝區容量,不需要動態擴充套件,但需要擷取當前緩衝區建立一個新的子緩衝區,具體的演算法如下:首先判斷下讀取索引是否小於新的容量值,如果小於進一步寫索引是否大於新的容量,如果大於則將寫索引設定為新的容量值。之後通過System.arraycopy將當前可讀的位元組陣列複製到新建立的子緩衝區。如果新的容量值小於讀索引,說明沒有可讀的位元組陣列需要複製到新建立的緩衝區中。

4.PooledHeapByteBuf

  PooledHeapByteBuf比UnpooledHeapByteBuf複雜一點,用到了執行緒池技術。首先來看看Recycler類。

/**
 * Light-weight object pool based on a thread-local stack.
 *
 * @param <T> the type of the pooled object
 */
public abstract class Recycler<T> {

    private final ThreadLocal<Stack<T>> threadLocal = new ThreadLocal<Stack<T>>() {
        @Override
        protected Stack<T> initialValue() {
            return new Stack<T>(Recycler.this, Thread.currentThread());
        }
    };
看註解就知道,Recycler是一個輕量級的執行緒池實現,通過定義了一個threadLocal,並初始化,看下初始化的詳細
 static final class Stack<T> implements Handle<T> {

        private static final int INITIAL_CAPACITY = 256;

        final Recycler<T> parent;
        final Thread thread;
        private T[] elements;
        private int size;
        private final Map<T, Boolean> map = new IdentityHashMap<T, Boolean>(INITIAL_CAPACITY);

        @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
        Stack(Recycler<T> parent, Thread thread) {
            this.parent = parent;
            this.thread = thread;
            elements = newArray(INITIAL_CAPACITY);
        }

Stack中定義了成員變數執行緒池、當前執行緒、陣列、數字大小、map ,map主要用來驗證執行緒池中是否已經存在。

繼續看,PooledByteBuf類繼承了AbstractReferenceCountedByteBuf,看下PooledByteBuf中定義的成員變數。
abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {

    private final Recycler.Handle<PooledByteBuf<T>> recyclerHandle;

    protected PoolChunk<T> chunk;
    protected long handle;
    protected T memory;
    protected int offset;
    protected int length;
    private int maxLength;

    private ByteBuffer tmpNioBuf;

    @SuppressWarnings("unchecked")
    protected PooledByteBuf(Recycler.Handle<? extends PooledByteBuf<T>> recyclerHandle, int maxCapacity) {
        super(maxCapacity);
        this.recyclerHandle = (Handle<PooledByteBuf<T>>) recyclerHandle;
    }
其中chunk主要用來組織和管理記憶體的分配和釋放。

5.ByteBufAllocator

  ByteBufAllocator是位元組緩衝區分配器,按照Netty的緩衝區實現的不同,共有兩者不同的分配器:基於記憶體池的位元組緩衝區分配器和普通的位元組緩衝區分配器。介面的繼承關係如下。


看下ByteBufAllocator中定義的常用介面

 /**
     * Allocate a {@link ByteBuf}. If it is a direct or heap buffer
     * depends on the actual implementation.
     */
    ByteBuf buffer();

    /**
     * Allocate a {@link ByteBuf} with the given initial capacity.
     * If it is a direct or heap buffer depends on the actual implementation.
     */
    ByteBuf buffer(int initialCapacity);

    /**
     * Allocate a {@link ByteBuf} with the given initial capacity and the given
     * maximal capacity. If it is a direct or heap buffer depends on the actual
     * implementation.
     */
    ByteBuf buffer(int initialCapacity, int maxCapacity);

    /**
     * Allocate a {@link ByteBuf} whose initial capacity is 0, preferably a direct buffer which is suitable for I/O.
     */
    ByteBuf ioBuffer();

    /**
     * Allocate a {@link ByteBuf}, preferably a direct buffer which is suitable for I/O.
     */
    ByteBuf ioBuffer(int initialCapacity);

    /**
     * Allocate a {@link ByteBuf}, preferably a direct buffer which is suitable for I/O.
     */
    ByteBuf ioBuffer(int initialCapacity, int maxCapacity);

    /**
     * Allocate a heap {@link ByteBuf}.
     */
    ByteBuf heapBuffer();

    /**
     * Allocate a heap {@link ByteBuf} with the given initial capacity.
     */
    ByteBuf heapBuffer(int initialCapacity);

    /**
     * Allocate a heap {@link ByteBuf} with the given initial capacity and the given
     * maximal capacity.
     */
    ByteBuf heapBuffer(int initialCapacity, int maxCapacity);

    /**
     * Allocate a direct {@link ByteBuf}.
     */
    ByteBuf directBuffer();

    /**
     * Allocate a direct {@link ByteBuf} with the given initial capacity.
     */
    ByteBuf directBuffer(int initialCapacity);

    /**
     * Allocate a direct {@link ByteBuf} with the given initial capacity and the given
     * maximal capacity.
     */
    ByteBuf directBuffer(int initialCapacity, int maxCapacity);

    

三.總結下

1.ByteBuffer必須自己長度固定,一旦分配完成,它的容量不能動態擴充套件和收縮;ByteBuf預設容器大小為256,支援動態擴容,在允許的最大擴容範圍內(Integer.MAX_VALUE)。

2.ByteBuffer只有一個標識位置的指標,讀寫的時候需要手動的呼叫flip()和rewind()等,否則很容易導致程式處理失敗。而ByteBuf有兩個標識位置的指標,一個寫writerIndex,一個讀readerIndex,讀寫的時候不需要呼叫額外的方法。

3.NIO的SocketChannel進行網路讀寫時,操作的物件是JDK標準的java.nio.byteBuffer。由於Netty使用統一的ByteBuf替代JDK原生的java.nio.ByteBuffer,所以ByteBuf中定義了ByteBuffer nioBuffer()方法將ByteBuf轉換成ByteBuffer。