Netty原始碼—六、tiny、small記憶體分配

highhand發表於2021-09-09

tiny記憶體分配

tiny記憶體分配流程:

  1. 如果申請的是tiny型別,會先從tiny快取中嘗試分配,如果快取分配成功則返回

  2. 否則從tinySubpagePools中嘗試分配

  3. 如果上面沒有分配成功則使用allocateNormal進行分配

從快取中分配

這裡以啟用了快取為例來說明,使用到的快取類是PoolThreadCache,快取是透過佇列實現的,一個佇列中儲存的記憶體大小都是相同的

// io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf, int)// 這裡的cache是PoolThreadCacheif (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {    // was able to allocate out of the cache so move on
    return;
}boolean allocateTiny(PoolArena> area, PooledByteBuf> buf, int reqCapacity, int normCapacity) {    // 快取維護了一個佇列,這個佇列中儲存的記憶體塊大小都相同
    // 找到快取之後,從佇列中取出一個記憶體塊用來初始化buffer
    return allocate(cacheForTiny(area, normCapacity), buf, reqCapacity);
}// 查詢快取陣列中快取的記憶體private MemoryRegionCache> cacheForTiny(PoolArena> area, int normCapacity) {    // 計算出申請記憶體位於快取陣列中的位置,即陣列下標
    int idx = PoolArena.tinyIdx(normCapacity);    if (area.isDirect()) {        // 使用直接記憶體的快取
        return cache(tinySubPageDirectCaches, idx);
    }    // 使用堆記憶體的快取
    return cache(tinySubPageHeapCaches, idx);
}static int tinyIdx(int normCapacity) {    // 由於tiny快取陣列大小是32,依次對應的記憶體大小是16、32...,512,所以陣列的下標應該是申請記憶體的大小除以16
    // normCapacity = normCapacity / 16
    return normCapacity >>> 4;
}

從快取中分配記憶體的過程

  1. 尋找快取tinySubPageDirectCaches

  2. 使用快取中的chunk初始化buf

關於tiny快取的資料結構

// tiny快取// 是一個SubPageMemoryRegionCache陣列,預設快取陣列長度:32,依次儲存的記憶體大小是16、32、48...,512io.netty.buffer.PoolThreadCache#tinySubPageDirectCaches// 初始化tiny快取陣列,陣列大小是32// static final int numTinySubpagePools = 512 >>> 4;tinySubPageDirectCaches = createSubPageCaches(
                    tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);// 初始化tiny和small快取陣列private static  MemoryRegionCache[] createSubPageCaches(    int cacheSize, int numCaches, SizeClass sizeClass) {    if (cacheSize > 0) {        @SuppressWarnings("unchecked")
        MemoryRegionCache[] cache = new MemoryRegionCache[numCaches];        for (int i = 0; i (cacheSize, sizeClass);
        }        return cache;
    } else {        return null;
    }
}

快取使用佇列實現,一個佇列最大元素個數

DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);

從tinySubpagePools中分配

上面快取中如果沒有分配到記憶體的話,會向記憶體池tinySubpagePools申請,主要邏輯是:

  1. 計算tinySubpagePools陣列的index,右移4,除以16(該陣列長度是512>>>4)

  2. 取出index出的subpage,也就是這個index處head

  3. 從head.next查詢合適的記憶體

  4. 找到可用記憶體後使用io.netty.buffer.PoolChunk#initBufWithSubpage(io.netty.buffer.PooledByteBuf

前面已經介紹過tinySubpagePools,是一個陣列,陣列大小是32,每個元素是一個PoolSubpage,PoolSubpage本身是一個連結串列,所以要在這個裡面查詢可用記憶體,先要計算出陣列下表,然後找到該位置的PoolSubpage,取出這個連結串列的頭,然後分配記憶體。關鍵程式碼如下

// io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf, int)private void allocate(PoolThreadCache cache, PooledByteBuf buf, final int reqCapacity) {    final int normCapacity = normalizeCapacity(reqCapacity);    if (isTinyOrSmall(normCapacity)) { // capacity [] table;        boolean tiny = isTiny(normCapacity);        if (tiny) { //  head = table[tableIdx];        /**
             * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
             * {@link PoolChunk#free(long)} may modify the doubly linked list as well.
             */
        synchronized (head) {            final PoolSubpage s = head.next;            // 空連結串列的話,head指向自己
            if (s != head) {                assert s.doNotDestroy && s.elemSize == normCapacity;                long handle = s.allocate();                assert handle >= 0;
                s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
                incTinySmallAllocation(tiny);                return;
            }
        }        // 省略中間程式碼... }

為什麼上面沒有沒有判斷head.next是否有可用的記憶體呢?

tinySubpagePools裡面儲存的是已經被分配過部分記憶體的PoolSubpage,PoolSubpage本身是一個連結串列,如果連結串列中除了head外有其他PoolSubpage,那麼這個subpage一定有可以用的記憶體塊,因為在PoolSubpage.allocate的時候,如果發現沒有可用的記憶體塊了,會將subpage從連結串列中移除。

按照normal的方式分配

如果上面兩種方法都沒有分配到記憶體,則呼叫allocateNormal方法來分配記憶體,allocateNormal獲取記憶體的方法前面已經說過了,這裡不再贅述。

small記憶體分配

small的分配過程和tiny記憶體的分配過程幾乎一致

small記憶體分配流程:

  1. 如果申請的是tiny型別,會先從small快取中嘗試分配,如果快取分配成功則返回

  2. 否則從smallSubpagePools中嘗試分配

  3. 如果上面沒有分配成功則使用allocateNormal進行分配

small記憶體的獲取過程和tiny的方式類似,可以對照學習,這裡不再詳述。

原文出處:https://www.cnblogs.com/sunshine-2015/p/9380751.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/810/viewspace-2805589/,如需轉載,請註明出處,否則將追究法律責任。

相關文章