Netty之內存分配器

為了降低分配和釋放內存的開銷,Netty通過ByteBufAllocator實現了ByteBuf的池化,并且可以用來分配我們想要的ByteBuf實例。

ByteBufAllocate有兩大子類:

  • PooledByteBufAllocator
  • UnpooledByteBufAllocator

UnpooledByteBufAllocator

關于堆內存的分配:

   protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
                : new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
   }

UnpooledUnsafeHeapByteBuf是UnpooledHeapByteBuf的子類,只是在獲取字節(jié)的時候底層采用了Unsafe操作。

關于堆外內存的分配:

@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);
}

在UnsafeDirectByteBuf中,有獲取內存地址,memoryAddress = PlatformDependent.directBufferAddress(buffer),然后在獲取的時候通過memoryAddress加上偏移量拿到字節(jié)。

Unsafe的一般是直接操作內存地址,那么相比于非Unsafe的速度應該會更快一些。

PooledByteBufAllocator

Pooled會更復雜一點,看下直接內存分配:

 protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        // 拿到線程局部緩存,不同的線程在不同的內存堆上分配,
        // threadCache是PoolThreadLocalCache,其繼承自ThreadLocal
        // PoolThreadCache是后面的重點
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        // 如果內存中不存在,則新建,否則直接使用內存中的
        ByteBuf buf;
        if (directArena != null) {
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            if (PlatformDependent.hasUnsafe()) {
                buf = UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
            } else {
                buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
            }
        }

        return toLeakAwareBuffer(buf);
    }
  • 從對象池中拿到PooledByteBuf進行復用。io.netty.buffer.PoolArena#newByteBuf

  • 然后從緩存上進行分配。io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)

  • 從內存堆上進行分配

內存規(guī)格

接著說下Netty內存相關的一些東西

0~512B Tiny
521B~8K small
8K~16M noarmal
16M以上 Huge

8K以下的內存交做subPage,8K的內存叫做Page,16M的內存空間也叫做Chunk

緩存的數據結構

Netty中與緩存相關的數據結構叫做MemoryRegionCache,io.netty.buffer.PoolThreadCache.MemoryRegionCache,它有三部分組成。

  • Queue:chunkHandler。
  • sizeClass:tiny,small,normal
  • size:N*16B,...
image-20190317102549130

PooledThreadLocalCache是每一個線程都會維護的緩存,每個緩存都維護了幾種不同大小的MemoryRegionCache。

image-20190317103050046

命中緩存分配

io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)

在方法中可以看出具體的分配流程。以其里面調用的io.netty.buffer.PoolThreadCache#allocateTiny為例

  1. allocate(cacheForTiny(area, normCapacity), buf, reqCapacity),首先調用cacheForTiny(area, normCapacity)找到一個對應size的MemoryRegionCache。
  2. 然后從找到的MemoryRegionCache的Queue中彈出一個Entry給ByteBuf初始化。
  3. 將彈出的Entry扔到對象池進行復用。io.netty.util.Recycler.Handle#recycle

最后

這是ByteBuf內存分配器常用的內存分配方法,了解下

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容