/* frag_pct 是从 jemalloc 获取的 */ /* Utility function to get the fragmentation ratio from jemalloc. * It is critical to do that by comparing only heap maps that belown to * jemalloc, and skip ones the jemalloc keeps as spare. Since we use this * fragmentation ratio in order to decide if a defrag action should be taken * or not, a false detection can cause the defragmenter to waste a lot of CPU * without the possibility of getting any results. */ floatgetAllocatorFragmentation(size_t *out_frag_bytes){ size_t epoch = 1, allocated = 0, resident = 0, active = 0, sz = sizeof(size_t); /* Update the statistics cached by mallctl. */ je_mallctl("epoch", &epoch, &sz, &epoch, sz); /* Unlike RSS, this does not include RSS from shared libraries and other non * heap mappings. */ je_mallctl("stats.resident", &resident, &sz, NULL, 0); /* Unlike resident, this doesn't not include the pages jemalloc reserves * for re-use (purge will clean that). */ je_mallctl("stats.active", &active, &sz, NULL, 0); /* Unlike zmalloc_used_memory, this matches the stats.resident by taking * into account all allocations done by this process (not only zmalloc). */ je_mallctl("stats.allocated", &allocated, &sz, NULL, 0); float frag_pct = ((float)active / allocated)*100 - 100; size_t frag_bytes = active - allocated; float rss_pct = ((float)resident / allocated)*100 - 100; size_t rss_bytes = resident - allocated; if(out_frag_bytes) *out_frag_bytes = frag_bytes; serverLog(LL_DEBUG, "allocated=%zu, active=%zu, resident=%zu, frag=%.0f%% (%.0f%% rss), frag_bytes=%zu (%zu%% rss)", allocated, active, resident, frag_pct, rss_pct, frag_bytes, rss_bytes); return frag_pct; }
if (server.aof_child_pid!=-1 || server.rdb_child_pid!=-1) return; /* Defragging memory while there's a fork will just do damage. */
/* Once a second, check if we the fragmentation justfies starting a scan * or making it more aggressive. */ run_with_period(1000) { size_t frag_bytes; float frag_pct = getAllocatorFragmentation(&frag_bytes); /* If we're not already running, and below the threshold, exit. */ if (!server.active_defrag_running) { if(frag_pct < server.active_defrag_threshold_lower || frag_bytes < server.active_defrag_ignore_bytes) return; }
/* 计算内存碎片整理所需要占用的主线程资源 */ int cpu_pct = INTERPOLATE(frag_pct, server.active_defrag_threshold_lower, server.active_defrag_threshold_upper, server.active_defrag_cycle_min, server.active_defrag_cycle_max); /* 限制占用资源范围 */ cpu_pct = LIMIT(cpu_pct, server.active_defrag_cycle_min, server.active_defrag_cycle_max); /* We allow increasing the aggressiveness during a scan, but don't * reduce it. */ if (!server.active_defrag_running || cpu_pct > server.active_defrag_running) { server.active_defrag_running = cpu_pct; serverLog(LL_VERBOSE, "Starting active defrag, frag=%.0f%%, frag_bytes=%zu, cpu=%d%%", frag_pct, frag_bytes, cpu_pct); } } if (!server.active_defrag_running) return;
/* See activeExpireCycle for how timelimit is handled. */ start = ustime(); /* 计算每次迭代的时间限制 */ timelimit = 1000000*server.active_defrag_running/server.hz/100; if (timelimit <= 0) timelimit = 1;
do { if (!cursor) { /* Move on to next database, and stop if we reached the last one. */ if (++current_db >= server.dbnum) { longlong now = ustime(); size_t frag_bytes; float frag_pct = getAllocatorFragmentation(&frag_bytes); serverLog(LL_VERBOSE, "Active defrag done in %dms, reallocated=%d, frag=%.0f%%, frag_bytes=%zu", (int)((now - start_scan)/1000), (int)(server.stat_active_defrag_hits - start_stat), frag_pct, frag_bytes);
start_scan = now; current_db = -1; cursor = 0; db = NULL; server.active_defrag_running = 0; return; } elseif (current_db==0) { /* Start a scan from the first database. */ start_scan = ustime(); start_stat = server.stat_active_defrag_hits; }
db = &server.db[current_db]; cursor = 0; }
do { cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db); /* Once in 16 scan iterations, or 1000 pointer reallocations * (if we have a lot of pointers in one hash bucket), check if we * reached the tiem limit. */ /* 一旦进入16次扫描迭代,或1000次指针重新分配(如果我们在一个散列桶中有很多指针),检查我们是否达到了tiem限制。*/ if (cursor && (++iterations > 16 || server.stat_active_defrag_hits - defragged > 1000)) { /* 如果超时则退出,等待下次获取线程资源后继续执行,*/ if ((ustime() - start) > timelimit) { return; } iterations = 0; defragged = server.stat_active_defrag_hits; } } while(cursor); } while(1); }