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