1. 程式人生 > >Netty原始碼分析第5章(ByteBuf)---->第2節: ByteBuf的分類

Netty原始碼分析第5章(ByteBuf)---->第2節: ByteBuf的分類

 

Netty原始碼分析第五章: ByteBuf

 

第二節: ByteBuf的分類

 

上一小節簡單介紹了AbstractByteBuf這個抽象類, 這一小節對其子類的分類做一個簡單的介紹

ByteBuf根據不同的分類方式, 會有不同的分類結果

我們首先看第一種分類方式:

1.Pooled和Unpooled:

pooled是從一塊記憶體裡去取一段連續記憶體封裝成byteBuf

具體標誌是類名以Pooled開頭的ByteBuf, 通常就是Pooled型別的ByteBuf, 比如: PooledDirectByteBuf或者pooledHeapByteBuf

有關如何分配一塊連續的記憶體, 我們之後的章節會講到

 

Unpooled是分配的時候直接呼叫系統api進行實現, 具體標誌是以Unpooled開頭的ByteBuf, 比如UnpooledDirectByteBuf, UnpooledHeapByteBuf

再看第二種分類方式:

2.基於直接記憶體的ByteBuf和基於堆記憶體的ByteBuf

基於直接記憶體的ByteBuf, 具體標誌是類名中包含單詞Direct的ByteBuf, 比如UnpooledDirectByteBuf, PooledDirectByteBuf等

基於堆記憶體的ByteBuf, 具體標誌是類名中包含單詞heap的ByteBuf, 比如UnpooledHeapByteBuf, PooledHeapByteBuf

 

結合以上兩種方式, 這裡通過其建立的方式去簡單對其分類做個解析

這裡第一種分類的Pooled, 也就是分配一塊連續記憶體建立byteBuf, 這一小節先不進行舉例, 會在之後的小結講到

這裡主要就看Unpooled, 也就是呼叫系統api的方式建立byteBuf, 在直接記憶體和堆記憶體中有什麼區別

這裡以UnpooledDirectByteBuf和UnpooledHeapByteBuf這兩種為例, 簡單介紹其建立方式:

首先看UnpooledHeapByteBuf的byetBuf, 這是基於記憶體建立ByteBuf, 並且是直接呼叫系統api

我們看UnpooledHeapByteBuf的byetBuf的構造方法:

 protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
     this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
 }

這裡呼叫了自身的構造方法, 引數中建立了新的位元組陣列, 初始長度為初始化的記憶體大小, 讀寫指標初始位置都是0, 並傳入了最大記憶體大小

從這裡看出, 有關堆記憶體的Unpooled型別的分配, 是通過位元組陣列進行實現的

再往下跟:

protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int maxCapacity) {
    this(alloc, initialArray, 0, initialArray.length, maxCapacity);
}

繼續跟:

private UnpooledHeapByteBuf(
        ByteBufAllocator alloc, byte[] initialArray, int readerIndex, int writerIndex, int maxCapacity) {
    super(maxCapacity);
    //忽略驗證程式碼
    this.alloc = alloc;
    setArray(initialArray);
    setIndex(readerIndex, writerIndex);
}

跟到setAarry方法中:

private void setArray(byte[] initialArray) {
    array = initialArray;
    tmpNioBuf = null;
}

將新建立的陣列賦值為自身的array屬性

回到建構函式中, 跟進setIndex方法:

public ByteBuf setIndex(int readerIndex, int writerIndex) {
    //忽略驗證程式碼
    setIndex0(readerIndex, writerIndex);
    return this;
}

這裡實際上是呼叫了AbstractByteBuf的setIndex方法

我們跟進setIndex0方法中:

final void setIndex0(int readerIndex, int writerIndex) {
    this.readerIndex = readerIndex;
    this.writerIndex = writerIndex;
}

這裡設定了讀寫指標, 根據之前的呼叫鏈我們知道, 這裡將讀寫指標位置都設定為了0

介紹完UnpooledHeapByteBuf的初始化, 我們繼續看UnpooledDirectByteBuf這個類的構造, 顧明思議, 是基於堆外記憶體, 並且同樣也是呼叫系統api的方式進行實現的

我們看其構造方法:

protected UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
    super(maxCapacity);
    //忽略驗證程式碼
    this.alloc = alloc;
    setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
}

我們關注下setByteBuffer中的引數ByteBuffer.allocateDirect(initialCapacity)

我們在這裡看到, 這裡通過jdk的ByteBuffer直接呼叫靜態方法allocateDirect分配了一個基於直接記憶體的ByteBuffer, 並設定了初始記憶體

再跟到setByteBuffer方法中:

private void setByteBuffer(ByteBuffer buffer) {
    ByteBuffer oldBuffer = this.buffer;
    if (oldBuffer != null) {
        //程式碼忽略
    }
    this.buffer = buffer;
    tmpNioBuf = null;
    capacity = buffer.remaining();
}

我們看到在這裡將分配的ByteBuf設定到當前類的成員變數中

 

以上兩種例項, 我們會對上面所講到的兩種分類有個初步的瞭解

這裡要注意一下, 基於堆記憶體建立ByteBuf, 可以不用考慮物件回收, 因為虛擬機器會進行垃圾回收, 但是堆外記憶體在虛擬機器的垃圾回收機制的作用域之外, 所以這裡要考慮手動回收物件

最後, 我們看第三種分類方式:

3.safe和unsafe

首先從名字上看, safe代表安全的, unsafe代表不安全的

這個安全與不安全的定義是什麼呢

其實在我們jdk裡面有unsafe物件, 可以通過unsafe物件直接拿到記憶體地址, 基於記憶體地址可以進行讀寫操作

如果是Usafe型別的byteBuf, 則可以直接拿到byteBuf在jvm中的具體記憶體, 可以通過呼叫jdk的Usafe物件進行讀寫, 所以這裡代表不安全

而非Usafe不能拿到jvm的具體記憶體, 所以這裡代表安全

具體標誌是如果類名中包含unsafe這個單詞的ByteBuf, 可以認為是一個unsafe型別的ByteBuf, 比如PooledUnsafeHeapByteBuf或者PooledUnsafeDirectByteBuf

以PooledUnsafeHeapByteBuf的_getByte方法為例:

protected byte _getByte(int index) {
    return UnsafeByteBufUtil.getByte(memory, idx(index));
}

這裡memory代表byebuffer底層分配記憶體的首地址, idx(index)代表當前指標index距記憶體memory的偏移地址, UnsafeByteBufUtil的getByte方法, 就可以直接通過這兩個資訊通過jdk底層的unsafe物件拿到jdk底層的值

有關AbstractByteBuf的主要實現類和繼承關係, 如下圖所示:

5-2-1