jemalloc横向分析(六)tcache_event事件执行

来源:互联网 发布:剑灵召唤师女捏脸数据 编辑:程序博客网 时间:2024/05/29 16:12
tcache_alloc_small最后调用了tcache_event
里面tcache->ev_cnt++;
当tcache->ev_cnt == TCACHE_GC_INCR(211)时
/*
 * TCACHE_GC_SWEEP is the approximate number of allocation events between
 * full GC sweeps.  Integer rounding may cause the actual number to be
 * slightly higher, since GC is performed incrementally.
 */
#define    TCACHE_GC_SWEEP            8192

/* Number of tcache allocation/deallocation events between incremental GCs. */
#define    TCACHE_GC_INCR                            \
    ((TCACHE_GC_SWEEP / NBINS) + ((TCACHE_GC_SWEEP / NBINS == 0) ? 0 : 1))
    
调用tcache_event_hard(tsd, tcache);
这个函数不区分当前正在使用哪个bin,而是通过tcache->next_gc_bin得到要处理的bin
处理完后,tcache->next_gc_bin++;

void
tcache_event_hard(tsd_t *tsd, tcache_t *tcache)
{
    szind_t binind = tcache->next_gc_bin;
    tcache_bin_t *tbin = &tcache->tbins[binind];
    tcache_bin_info_t *tbin_info = &tcache_bin_info[binind];

    if (tbin->low_water > 0) {
        /*
         * Flush (ceiling) 3/4 of the objects below the low water mark.
         */第一次进到这个分支的是2号bin
         {tstats = {nrequests = 62}, low_water = 38, lg_fill_div = 1, ncached = 38, avail = 0x7ffff660e220}
        在低水位标记冲刷四分之三的对象,
        此时已分配了62次,从数组的尾部开始的,也就是从头开始的38个还是空闲的
        if (binind < NBINS) {
            tcache_bin_flush_small(tsd, tcache, tbin, binind,
                tbin->ncached - tbin->low_water + (tbin->low_water
                >> 2));
                ncached - low_water + 1/4 * low_water
                ncached - 3/4 * low_water = 9(一开始ncached和low_water是相等的)
                只保留这么多(9个),刷掉3/4 * low_water个
        } else {
            tcache_bin_flush_large(tsd, tbin, binind, tbin->ncached
                - tbin->low_water + (tbin->low_water >> 2), tcache);
        }
        /*
         * Reduce fill count by 2X.  Limit lg_fill_div such that the
         * fill count is always at least 1.
         */
        if ((tbin_info->ncached_max >> (tbin->lg_fill_div+1)) >= 1)
            tbin->lg_fill_div++;
    } else if (tbin->low_water < 0) {
        /*
         * Increase fill count by 2X.  Make sure lg_fill_div stays
         * greater than 0.
         */任何一个bin,在进这个函数前,low_water=-1,lg_fill_div=1
        if (tbin->lg_fill_div > 1)
            tbin->lg_fill_div--; 不会执行
    }
    tbin->low_water = tbin->ncached; 26(这里处理的是0号bin)

    tcache->next_gc_bin++;轮询处理下一个bin
    if (tcache->next_gc_bin == nhbins)
        tcache->next_gc_bin = 0;
    tcache->ev_cnt = 0; 事件重置为0
}

void
tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
    szind_t binind, unsigned rem)
{现在缓存了ncached个,只保留rem个,冲掉ncached-rem个
    arena_t *arena;
    void *ptr;
    unsigned i, nflush, ndeferred;
    bool merged_stats = false;

    assert(binind < NBINS);2
    assert(rem <= tbin->ncached);9

    arena = arena_choose(tsd, NULL);
    assert(arena != NULL);
    for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) {
        /* Lock the arena bin associated with the first object. */
        arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(
            tbin->avail[0]);
        arena_t *bin_arena = extent_node_arena_get(&chunk->node);
        arena_bin_t *bin = &bin_arena->bins[binind];

        if (config_prof && bin_arena == arena) {
            if (arena_prof_accum(arena, tcache->prof_accumbytes))
                prof_idump();
            tcache->prof_accumbytes = 0;
        }

        malloc_mutex_lock(&bin->lock);
        if (config_stats && bin_arena == arena) {
            assert(!merged_stats);
            merged_stats = true;
            bin->stats.nflushes++;
            bin->stats.nrequests += tbin->tstats.nrequests;
            tbin->tstats.nrequests = 0;
        }
        ndeferred = 0;
        for (i = 0; i < nflush; i++) { 从0开始冲刷,总共要冲刷nflush个
            ptr = tbin->avail[i];
            assert(ptr != NULL);
            chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
            if (extent_node_arena_get(&chunk->node) == bin_arena) {
                size_t pageind = ((uintptr_t)ptr -
                    (uintptr_t)chunk) >> LG_PAGE;
                arena_chunk_map_bits_t *bitselm =
                    arena_bitselm_get(chunk, pageind);
                arena_dalloc_bin_junked_locked(bin_arena, chunk,
                    ptr, bitselm);
                    对这个对象进行dalloc
            } else {
                /*
                 * This object was allocated via a different
                 * arena bin than the one that is currently
                 * locked.  Stash the object, so that it can be
                 * handled in a future pass.
                 */这个对象是通过不同的area分配的,贮藏这个对象,以便在将来走到这个函数它能够被处理
                tbin->avail[ndeferred] = ptr;
                ndeferred++;因为是从0开始flush的,ndeferred也是从0递增的,所以ndeferred是空闲的
            }
        }
        malloc_mutex_unlock(&bin->lock);
    }
    if (config_stats && !merged_stats) {
        /*
         * The flush loop didn't happen to flush to this thread's
         * arena, so the stats didn't get merged.  Manually do so now.
         */
        arena_bin_t *bin = &arena->bins[binind];
        malloc_mutex_lock(&bin->lock);
        bin->stats.nflushes++;
        bin->stats.nrequests += tbin->tstats.nrequests;
        tbin->tstats.nrequests = 0;
        malloc_mutex_unlock(&bin->lock);
    }

    memmove(tbin->avail, &tbin->avail[tbin->ncached - rem],
        rem * sizeof(void *));
    tbin->ncached = rem;
    0-3/4的对象都冲刷的,把剩余的1/4挪到avail数组的开始
    if ((int)tbin->ncached < tbin->low_water)
        tbin->low_water = tbin->ncached; low_water始终不能超过ncached
}

下面研究arena_dalloc_bin_junked_locked(bin_arena, chunk, ptr, bitselm);
arena_dalloc_bin_locked_impl(arena, chunk, ptr, bitselm, true);
static void
arena_dalloc_bin_locked_impl(arena_t *arena, arena_chunk_t *chunk, void *ptr,
    arena_chunk_map_bits_t *bitselm, bool junked)
{
    size_t pageind, rpages_ind;
    arena_run_t *run;
    arena_bin_t *bin;
    arena_bin_info_t *bin_info;
    szind_t binind;

    pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; 这个地址是在33页
    rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, pageind);
        第33页的runid是2,相对这个run的起始页的页号偏移
        (gdb) p je_arena_bin_info[2]
        $4 = {reg_size = 24, redzone_size = 0, reg_interval = 24, run_size = 12288, nregs = 512, bitmap_info = {nbits = 512,
            nlevels = 2, levels = {{group_offset = 0}, {group_offset = 8}, {group_offset = 9}, {group_offset = 0}}},
          reg0_offset = 0}
        (gdb) p 12288/4096
        $5 = 3
        2号bin的run_size是3页
    run = &arena_miscelm_get(chunk, rpages_ind)->run;通过起始页的misc得到这个run
    binind = run->binind;
    bin = &arena->bins[binind];
    bin_info = &arena_bin_info[binind];

    if (!junked && config_fill && unlikely(opt_junk_free))
        arena_dalloc_junk_small(ptr, bin_info);

    arena_run_reg_dalloc(run, ptr);
        寄存器级别的释放
    if (run->nfree == bin_info->nregs) {
        arena_dissociate_bin_run(chunk, run, bin);
        arena_dalloc_bin_run(arena, chunk, run, bin);
    } else if (run->nfree == 1 && run != bin->runcur)
        arena_bin_lower_run(arena, chunk, run, bin);

    if (config_stats) {
        bin->stats.ndalloc++;
        bin->stats.curregs--;
    }
}

JEMALLOC_INLINE_C void
arena_run_reg_dalloc(arena_run_t *run, void *ptr)
{
    arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
    size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
    size_t mapbits = arena_mapbits_get(chunk, pageind);
    szind_t binind = arena_ptr_small_binind_get(ptr, mapbits);
    arena_bin_info_t *bin_info = &arena_bin_info[binind]; 上面这些都介绍过
    unsigned regind = arena_run_regind(run, bin_info, ptr);
        怎么通过ptr得到regind
    assert(run->nfree < bin_info->nregs);
    /* Freeing an interior pointer can cause assertion failure. */
    assert(((uintptr_t)ptr -
        ((uintptr_t)arena_miscelm_to_rpages(arena_run_to_miscelm(run)) +
        (uintptr_t)bin_info->reg0_offset)) %
        (uintptr_t)bin_info->reg_interval == 0);
    assert((uintptr_t)ptr >=
        (uintptr_t)arena_miscelm_to_rpages(arena_run_to_miscelm(run)) +
        (uintptr_t)bin_info->reg0_offset);
    /* Freeing an unallocated pointer can cause assertion failure. */
    assert(bitmap_get(run->bitmap, &bin_info->bitmap_info, regind));

    bitmap_unset(run->bitmap, &bin_info->bitmap_info, regind);位图置1,表示空闲,和bitmap_set类似
    run->nfree++; 空闲数量加1
}

JEMALLOC_INLINE unsigned
arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr)
{
    unsigned shift, diff, regind;
    size_t interval;
    arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run);得到这个run对应的misc
    void *rpages = arena_miscelm_to_rpages(miscelm);
    得到这个misc对应的页的地址
    /*
     * Freeing a pointer lower than region zero can cause assertion
     * failure.
     */
    assert((uintptr_t)ptr >= (uintptr_t)rpages +
        (uintptr_t)bin_info->reg0_offset);

    /*
     * Avoid doing division with a variable divisor if possible.  Using
     * actual division here can reduce allocator throughput by over 20%!
     */如果可能,尽量避免对一个变量做除法
    diff = (unsigned)((uintptr_t)ptr - (uintptr_t)rpages -
        bin_info->reg0_offset);计算要释放的地址和run的起始地址的距离,9576

    /* Rescale (factor powers of 2 out of the numerator and denominator). */
    重新调节(2的多少次方超过分子和分母)
    interval = bin_info->reg_interval; 24
    shift = jemalloc_ffs(interval) - 1; 0x18,最右边第一个1是第4位(从右从1开始)
        3,将间隔末尾的0都去掉
    diff >>= shift; 1197
    interval >>= shift; 3 都缩小8倍

    if (interval == 1) {如果间隔是2的某一次方
        /* The divisor was a power of 2. */
        regind = diff; 右移后的diff就是第几个reg
    } else {
        /*
         * To divide by a number D that is not a power of two we
         * multiply by (2^21 / D) and then right shift by 21 positions.
         * D不是2的某一次方,要除D,可以先乘(2^21 / D)再右移21位
         *   X / D
         *
         * becomes
         *
         *   (X * interval_invs[D - 3]) >> SIZE_INV_SHIFT
        
         * X/D于是就变成了(X * interval_invs[D - 3]) >> SIZE_INV_SHIFT
          X代表diff右移后的结果,D代表interval右移的结果
        
         * We can omit the first three elements, because we never
         * divide by 0, and 1 and 2 are both powers of two, which are
         * handled above.
         */我们能省略开始的3个元素,因为从来不会除0,1和2又都是2的某一次方,上面已经处理过了
#define    SIZE_INV_SHIFT    ((sizeof(unsigned) << 3) - LG_RUN_MAXREGS)
    ((sizeof(4) << 3) - 9) = 23
#define    SIZE_INV(s)    (((1U << SIZE_INV_SHIFT) / (s)) + 1)
    (((1U << 23) / (s)) + 1)
        static const unsigned interval_invs[] = { 函数内申明了一个数组
            SIZE_INV(3), 2^23/3 + 1
            SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7),
            SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11),
            SIZE_INV(12), SIZE_INV(13), SIZE_INV(14), SIZE_INV(15),
            SIZE_INV(16), SIZE_INV(17), SIZE_INV(18), SIZE_INV(19),
            SIZE_INV(20), SIZE_INV(21), SIZE_INV(22), SIZE_INV(23),
            SIZE_INV(24), SIZE_INV(25), SIZE_INV(26), SIZE_INV(27),
            SIZE_INV(28), SIZE_INV(29), SIZE_INV(30), SIZE_INV(31)
        };

        if (likely(interval <= ((sizeof(interval_invs) /
            sizeof(unsigned)) + 2))) {
            regind = (diff * interval_invs[interval - 3]) >>
                SIZE_INV_SHIFT;乘以某个数再右移
        } else
            regind = diff / interval;
#undef SIZE_INV
#undef SIZE_INV_SHIFT
    }
    assert(diff == regind * interval);
    assert(regind < bin_info->nregs);

    return (regind);
}