Netty源码分析(十) PoolArena

2018-11-08  本文已影响0人  skyguard

下面我们来说说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的分析就到这里了。

上一篇下一篇

猜你喜欢

热点阅读