Redis 自行實作記憶體分配器封裝層 zmalloc,支援 libc、jemalloc、tcmalloc 多種後端,並提供記憶體使用量統計、碎片率計算、執行緒安全、OOM 處理等功能。本文對 zmalloc.h、zmalloc.c 進行逐段註解分析。
USE_TCMALLOC:Google tcmalloc,需版本 ≥ 1.6,提供 tc_malloc_size() 取得區塊大小。USE_JEMALLOC:Facebook jemalloc,需版本 ≥ 2.1,提供 je_malloc_usable_size()。__APPLE__:macOS 使用 malloc_size()。malloc/free,自行在區塊前綴記錄大小 (PREFIX_SIZE)。void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void); // 總分配量
void zmalloc_enable_thread_safeness(void); // 啟用執行緒安全
void zmalloc_set_oom_handler(void (*)(size_t)); // 設定 OOM 回呼
float zmalloc_get_fragmentation_ratio(size_t rss); // 碎片率 = RSS / allocated
size_t zmalloc_get_rss(void); // 取得駐留集大小 (OS 特定)
size_t zmalloc_get_private_dirty(void); // 取得 Private Dirty (Linux /proc/smaps)
// 支援三種原子層級:GCC __atomic、__sync、pthread_mutex
#define update_zmalloc_stat_add(__n) __atomic_add_fetch(&used_memory, __n, __ATOMIC_RELAXED)
#define update_zmalloc_stat_sub(__n) __atomic_sub_fetch(&used_memory, __n, __ATOMIC_RELAXED)
// 分配時:對齊到 sizeof(long) + 更新統計
#define update_zmalloc_stat_alloc(__n) do {
size_t _n = (__n);
if (_n & (sizeof(long)-1)) _n += sizeof(long) - (_n & (sizeof(long)-1));
if (zmalloc_thread_safe) update_zmalloc_stat_add(_n);
else used_memory += _n;
} while(0)
// 釋放時:同樣對齊 + 扣減統計
#define update_zmalloc_stat_free(__n) do {
size_t _n = (__n);
if (_n & (sizeof(long)-1)) _n += sizeof(long) - (_n & (sizeof(long)-1));
if (zmalloc_thread_safe) update_zmalloc_stat_sub(_n);
else used_memory -= _n;
} while(0)
zmalloc()void *zmalloc(size_t size) {
void *ptr = malloc(size + PREFIX_SIZE); // libc 模式額外分配前綴
if (!ptr) zmalloc_oom_handler(size); // OOM 回呼 (預設列印錯誤並 abort)
#ifdef HAVE_MALLOC_SIZE
// jemalloc/tcmalloc/macOS: 直接問分配器實際大小
update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr;
#else
// libc: 自己在前綴寫入 size
*((size_t*)ptr) = size;
update_zmalloc_stat_alloc(size + PREFIX_SIZE);
return (char*)ptr + PREFIX_SIZE; // 回傳使用者資料起始位址
#endif
}
zrealloc()zmalloc_size 或前綴)。realloc。zfree()free()。size_t zmalloc_used_memory(void) {
if (zmalloc_thread_safe) {
// 原子讀取或 mutex 保護
return __atomic_load_n(&used_memory, __ATOMIC_RELAXED);
}
return used_memory;
}
解析 /proc/<pid>/stat 第 24 欄位 (RSS 頁數) × sysconf(_SC_PAGESIZE)。
float zmalloc_get_fragmentation_ratio(size_t rss) {
return (float)rss / zmalloc_used_memory();
}
activedefrag。解析 /proc/self/smaps 中 Private_Dirty: 欄位,反映進程實際獨佔髒頁,比 RSS 更精準衡量 Redis 記憶體壓力。
sizeof(long),減少偽共享。__atomic → __sync → mutex,效能最佳化。INFO MEMORY。mem_fragmentation_ratio,持續 > 1.5 考慮重啟或升級 jemalloc 5.x。zmalloc_get_private_dirty() 判斷實際記憶體壓力,避免 RSS 誤導 (含共享庫、頁面快取)。zmalloc_enable_thread_safeness() (Redis 啟動時自動啟用)。