Netty源碼解析之ByteBuf簡析

ByteBuf是什麼?
首先當前類是ByteBuf.java類,是netty緩存的抽象類。主要包含如下幾個Index
0      <=      readerIndex   <=   writerIndex    <=    capacity
包含的方法大致如下
setXxx        //設置當前index對應的值
readXxx        //讀
WriteXxx        //寫
markXxx        //標記當前index
resetXxx        //reset到上面mark的index
其中AbstractByteBuf繼承實現了某些具體的類,其繼承關係如下:
 
 
主要實現了ByteBuf的 讀寫、mark、reset等方法,詳情不細說,有興趣自己看。其中裏面很多方法都僅做聲明(模板方法模式),如
 
 
繼續往下跟可以發現如下結構圖
 
 
ByteBuf分爲safe和unsafe方式取數據
  • safe方式:通過內存地址+偏移量 獲取數據
  • unsafe方式:數組+下標   或    jdk底層byteBuf api拿數據
ByteBuf可分爲Heap(堆內存)、Direct(直接內存),往下根據源碼可知道
  • Heap中使用的是byte[]數據,Direct中使用的是直接內存(依賴JDK底層的DirectByteBuf,需要自己手動釋放)
 

Netty內存管理器
當前內存管理的繼承關係圖如下(ByteBufAllocatr是最上層):
 
 
 
接下來我們來看看UnpooledByteBufAllocator和PooledByteBufAllocator的實現和區別
1 均繼承AbstractByteBufAllocator,但UnpooledByteBufAllocator更像是一個簡化的內存分配器
 
 
 
 
2 先分析UnpooledByteBufAllocator,可發現其方法僅有6個如下截圖(接下來大致分析一下):
 
 
先看其構造方法如下
// UnpooledByteBufAllocator.java
public UnpooledByteBufAllocator(boolean preferDirect, boolean disableLeakDetector) {
    super(preferDirect);   //preferDirect用於決定是否使用directBuf
    this.disableLeakDetector = disableLeakDetector;
}
 
// AbstractByteBufAllocator.java
protected AbstractByteBufAllocator(boolean preferDirect) {
    // 需要兩者都爲true才標記爲使用directBuf,即需要爲unsafe
    directByDefault = preferDirect && PlatformDependent.hasUnsafe();
    emptyBuf = new EmptyByteBuf(this);
}
 
接下來看一下是如何分配的(從下面就可以判斷當前方法都會判斷是否safe,然後分別創建不同的buf)
// UnpooledByteBufAllocator.java
@Override
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
    return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) : new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
}
 
@Override
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
    ByteBuf buf = PlatformDependent.hasUnsafe() ?
    UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) : new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
 
    return disableLeakDetector ? buf : toLeakAwareBuffer(buf);
}
 
3 上面簡單的分析了Unpool的,接下來就不分析下Pool的(Pool的比較長,會單獨出來講解),注意的一點是Pool用的是
    private final PoolThreadLocalCache threadCache;
PoolThreadLocalCache繼承了FastThreadLocal,是netty對ThreadLocal的一種優化,其主要優化了如下,可參見:https://www.jianshu.com/p/3fc2fbac4bb7 + https://blog.csdn.net/lirenzuo/article/details/94495469
    - 不使用jdk線性探測法的 Map,自定義了InternalThreadLocalMap,使用的是下標定位,所以更快,並且不會存在內存泄漏風險
 

下面繼續講解一下PoolByteBufAllocator.java中是如何分配內存的,其分配內存過程如下:
取一個實例化buffer的方法解析如下:
// PoolByteBufAllocator.java
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
    PoolThreadCache cache = threadCache.get();
     // 首先可看出heapArena是從cache中取出的,解析來我們可以看到分配的過程中涉及到了很多緩存
    PoolArena<byte[]> heapArena = cache.heapArena;
 
    ByteBuf buf;    
    if (heapArena != null) {
        buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
    } else {
        buf = new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }
 
    return toLeakAwareBuffer(buf);
}
 
上面我們主要解析PoolArena是如何處理的,跟蹤heapArena.allocate()方法如下
// PoolArena.java
PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
    // 第一步:實例化一個buffer
    PooledByteBuf<T> buf = newByteBuf(maxCapacity);
    // 第二步:分配內存
    allocate(cache, buf, reqCapacity);
    return buf;
}
說明:上面主要分爲兩步,接下來會一個個簡析
 
  • 第一步:實例化一個buffer(跟蹤到如下)
// PooledUnsafeDirectByteBuf.java
static PooledUnsafeDirectByteBuf newInstance(int maxCapacity) {
    // 他是從回收的對象池(RECYCLER.get())中直接複用,而不會重新new一個
    PooledUnsafeDirectByteBuf buf = RECYCLER.get();
    buf.reuse(maxCapacity);
    return buf;
}
 
  • 第二步:分配內存
    • 分配內存這個會優先從緩存中進行分配(已使用並釋放的),找一個差不多大小的緩存來分配掉,沒有命中緩存才進行實際的內存分配。
// 以下的代碼是優先找一個緩存進行分配的示例
if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
    // was able to allocate out of the cache so move on
    return;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章