• 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的方式类似,可以对照学习,这里不再详述。

  • 相关阅读:
    JZOJ 1075. 【GDKOI2006】新红黑树
    [CQOI2018]异或序列
    JZOJ 1077. 【GDKOI2006】防御力量
    [TJOI2014]匹配
    JZOJ 1073. 【GDOI2005】山海经
    JZOJ 3745. 【NOI2014模拟7.14】Problem A
    SQL Server 2008 数据库快照
    vSphere 5.0 开发系列(一)vSphere 5.0 环境搭建手顺
    Crm系统帮助
    SQL Server 2008 Entity Data Model 和 Linq to Entities
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/9380751.html
Copyright © 2020-2023  润新知