zoukankan      html  css  js  c++  java
  • Netty源码—六、tiny、small内存分配

    tiny内存分配

    tiny内存分配流程:

    1. 如果申请的是tiny类型,会先从tiny缓存中尝试分配,如果缓存分配成功则返回

    2. 否则从tinySubpagePools中尝试分配

    3. 如果上面没有分配成功则使用allocateNormal进行分配

    从缓存中分配

    这里以启用了缓存为例来说明,使用到的缓存类是PoolThreadCache,缓存是通过队列实现的,一个队列中存储的内存大小都是相同的

    // io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)
    // 这里的cache是PoolThreadCache
    if (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...,512
    io.netty.buffer.PoolThreadCache#tinySubPageDirectCaches
    
    // 初始化tiny缓存数组,数组大小是32
    // static final int numTinySubpagePools = 512 >>> 4;
    tinySubPageDirectCaches = createSubPageCaches(
                        tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
    
    
    // 初始化tiny和small缓存数组
    private static <T> MemoryRegionCache<T>[] createSubPageCaches(
        int cacheSize, int numCaches, SizeClass sizeClass) {
        if (cacheSize > 0) {
            @SuppressWarnings("unchecked")
            MemoryRegionCache<T>[] cache = new MemoryRegionCache[numCaches];
            for (int i = 0; i < cache.length; i++) {
                // TODO: maybe use cacheSize / cache.length
                cache[i] = new SubPageMemoryRegionCache<T>(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, long, int)初始化buffer

    前面已经介绍过tinySubpagePools,是一个数组,数组大小是32,每个元素是一个PoolSubpage,PoolSubpage本身是一个链表,所以要在这个里面查找可用内存,先要计算出数组下表,然后找到该位置的PoolSubpage,取出这个链表的头,然后分配内存。关键代码如下

    // io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)
    private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
        final int normCapacity = normalizeCapacity(reqCapacity);
        if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
            int tableIdx;
            PoolSubpage<T>[] table;
            boolean tiny = isTiny(normCapacity);
            if (tiny) { // < 512
                // 省略中间代码...
                tableIdx = tinyIdx(normCapacity);
                table = tinySubpagePools;
            } else {
                // 省略中间代码...
            }
    
            final PoolSubpage<T> 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<T> 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的方式类似,可以对照学习,这里不再详述。

  • 相关阅读:
    173. Binary Search Tree Iterator
    199. Binary Tree Right Side View
    230. Kth Smallest Element in a BST
    236. Lowest Common Ancestor of a Binary Tree
    337. House Robber III
    449. Serialize and Deserialize BST
    508. Most Frequent Subtree Sum
    513. Find Bottom Left Tree Value
    129. Sum Root to Leaf Numbers
    652. Find Duplicate Subtrees
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/9380751.html
Copyright © 2011-2022 走看看