美文网首页
Netty源码分析(十) PoolArena

Netty源码分析(十) PoolArena

作者: skyguard | 来源:发表于2018-11-08 10:09 被阅读0次

    下面我们来说说Netty内存管理的PoolArena。之前我们说过的PoolChunk,PoolChunkList都属于一个PoolArena,通过PoolArena来管理PoolChunk和PoolChunkList。虽然提供了多个PoolArena减少线程间的竞争,但是难免还是会存在锁竞争,所以需要利用ThreaLocal进一步优化,把已申请的内存放入到ThreaLocal自然就没有竞争了。大体思路是在ThreadLocal里面放一个PoolThreadCache对象,然后释放的内存都放入到PoolThreadCache里面,下次申请先从PoolThreadCache获取。
    但是,如果thread1申请了一块内存,然后传到thread2在线程释放,这个Netty在内存holder对象里面会引用PoolThreadCache,所以还是会释放到thread1里。先来看下PoolArena都有哪些属性

    /**
     * {@link #tinySubpagePools} 数组的大小
     *
     * 默认为 32 。
     */
    static final int numTinySubpagePools = 512 >>> 4;
    
    /**
     * 所属 PooledByteBufAllocator 对象
     */
    final PooledByteBufAllocator parent;
    
    /**
     * 满二叉树的高度。默认为 11 。
     */
    private final int maxOrder;
    /**
     * Page 大小,默认 8KB = 8192B
     */
    final int pageSize;
    /**
     * 从 1 开始左移到 {@link #pageSize} 的位数。默认 13 ,1 << 13 = 8192 。
     */
    final int pageShifts;
    /**
     * Chunk 内存块占用大小。默认为 16M = 16 * 1024  。
     */
    final int chunkSize;
    /**
     * 判断分配请求内存是否为 Tiny/Small ,即分配 Subpage 内存块。
     *
     * Used to determine if the requested capacity is equal to or greater than pageSize.
     */
    final int subpageOverflowMask;
    
    /**
     * {@link #smallSubpagePools} 数组的大小
     *
     * 默认为 4
     */
    final int numSmallSubpagePools;
    
    /**
     * 对齐基准
     */
    final int directMemoryCacheAlignment;
    /**
     * {@link #directMemoryCacheAlignment} 掩码
     */
    final int directMemoryCacheAlignmentMask;
    
    /**
     * tiny 类型的 PoolSubpage 数组
     *
     * 数组的每个元素,都是双向链表
     */
    private final PoolSubpage<T>[] tinySubpagePools;
    /**
     * small 类型的 SubpagePools 数组
     *
     * 数组的每个元素,都是双向链表
     */
    private final PoolSubpage<T>[] smallSubpagePools;
    

    再来看下PoolArena是怎么分配内存的

    private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
        // 标准化请求分配的容量
        final int normCapacity = normalizeCapacity(reqCapacity);
        // PoolSubpage 的情况
        if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
            int tableIdx;
            PoolSubpage<T>[] table;
            // 判断是否为 tiny 类型的内存块申请
            boolean tiny = isTiny(normCapacity);
            if (tiny) { // < 512 tiny 类型的内存块申请
                // 从 PoolThreadCache 缓存中,分配 tiny 内存块,并初始化到 PooledByteBuf 中。
                if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                // 获得 tableIdx 和 table 属性
                tableIdx = tinyIdx(normCapacity);
                table = tinySubpagePools;
            } else {
                // 从 PoolThreadCache 缓存中,分配 small 内存块,并初始化到 PooledByteBuf 中。
                if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                // 获得 tableIdx 和 table 属性
                tableIdx = smallIdx(normCapacity);
                table = smallSubpagePools;
            }
    
            // 获得 PoolSubpage 链表的头节点
            final PoolSubpage<T> head = table[tableIdx];
    
            // 从 PoolSubpage 链表中,分配 Subpage 内存块
            /**
             * 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) { // 同步 head ,避免并发问题
                final PoolSubpage<T> s = head.next;
                if (s != head) {
                    assert s.doNotDestroy && s.elemSize == normCapacity;
                    // 分配 Subpage 内存块
                    long handle = s.allocate();
                    assert handle >= 0;
                    // 初始化 Subpage 内存块到 PooledByteBuf 对象中
                    s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
                    // 增加 allocationsTiny 或 allocationsSmall 计数
                    incTinySmallAllocation(tiny);
                    // 返回,因为已经分配成功
                    return;
                }
            }
            // 申请 Normal Page 内存块。实际上,只占用其中一块 Subpage 内存块。
            synchronized (this) { // 同步 arena ,避免并发问题
                allocateNormal(buf, reqCapacity, normCapacity);
            }
            // 增加 allocationsTiny 或 allocationsSmall 计数
            incTinySmallAllocation(tiny);
            // 返回,因为已经分配成功
            return;
        }
        if (normCapacity <= chunkSize) {
            // 从 PoolThreadCache 缓存中,分配 normal 内存块,并初始化到 PooledByteBuf 中。
            if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
                // was able to allocate out of the cache so move on
                return;
            }
            // 申请 Normal Page 内存块
            synchronized (this) { // 同步 arena ,避免并发问题
                allocateNormal(buf, reqCapacity, normCapacity);
                // 增加 allocationsNormal
                ++allocationsNormal;
            }
        } else {
            // 申请 Huge Page 内存块
            // Huge allocations are never served via the cache so just call allocateHuge
            allocateHuge(buf, reqCapacity);
        }
    }
    

    就是根据内存的大小,分配不同类型的内存,调用PoolSubpage或PoolChunk来分配内存。
    PoolArena的分析就到这里了。

    相关文章

      网友评论

          本文标题:Netty源码分析(十) PoolArena

          本文链接:https://www.haomeiwen.com/subject/oifbxqtx.html