首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> DB2 資料庫 >> DB2 教學

Redis 記憶體管理原始碼深度註解 (zmalloc.h / zmalloc.c)

     編輯:DB2 教學

前言

Redis 自行實作記憶體分配器封裝層 zmalloc,支援 libc、jemalloc、tcmalloc 多種後端,並提供記憶體使用量統計、碎片率計算、執行緒安全、OOM 處理等功能。本文對 zmalloc.h、zmalloc.c 進行逐段註解分析。

zmalloc.h - 介面定義

後端選擇與巨集

  • USE_TCMALLOC:Google tcmalloc,需版本 ≥ 1.6,提供 tc_malloc_size() 取得區塊大小。
  • USE_JEMALLOC:Facebook jemalloc,需版本 ≥ 2.1,提供 je_malloc_usable_size()。
  • __APPLE__:macOS 使用 malloc_size()。
  • 預設 fallback:libc malloc/free,自行在區塊前綴記錄大小 (PREFIX_SIZE)。

核心 API

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)

zmalloc.c - 實作細節

1. 記憶體統計更新巨集 (原子操作)

// 支援三種原子層級: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)

2. 分配核心 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
}

3. 重新分配 zrealloc()

  • 先取得舊大小 (zmalloc_size 或前綴)。
  • 呼叫底層 realloc。
  • 統計:扣減舊大小、加上新大小。
  • libc 模式需更新新區塊前綴。

4. 釋放 zfree()

  • 依模式取得實際大小。
  • 統計扣減。
  • libc 模式必須傳回含前綴的原始指標給 free()。

5. 記憶體用量查詢

size_t zmalloc_used_memory(void) {
    if (zmalloc_thread_safe) {
        // 原子讀取或 mutex 保護
        return __atomic_load_n(&used_memory, __ATOMIC_RELAXED);
    }
    return used_memory;
}

6. RSS 取得 (Linux)

解析 /proc/<pid>/stat 第 24 欄位 (RSS 頁數) × sysconf(_SC_PAGESIZE)。

7. 碎片率計算

float zmalloc_get_fragmentation_ratio(size_t rss) {
    return (float)rss / zmalloc_used_memory();
}
  • ≈ 1.0:低碎片。
  • > 1.5:高碎片,建議重啟或啟用 activedefrag。

8. Private Dirty 取得

解析 /proc/self/smaps 中 Private_Dirty: 欄位,反映進程實際獨佔髒頁,比 RSS 更精準衡量 Redis 記憶體壓力。

關鍵設計亮點

  1. 多分配器抽象:編譯期切換,運行期零成本。
  2. 前綴大小記錄:libc 模式自建元資料,相容性最佳。
  3. 長度對齊:統計量對齊 sizeof(long),減少偽共享。
  4. 原子操作分級:優先 __atomic → __sync → mutex,效能最佳化。
  5. OOM 可擴充:支援自訂處理器 (如拒絕指令而非 crash)。
  6. 記憶體診斷完整:used_memory、RSS、fragmentation、private_dirty 四大指標支援 INFO MEMORY。

實務調優啟示

  • 監控 mem_fragmentation_ratio,持續 > 1.5 考慮重啟或升級 jemalloc 5.x。
  • 使用 zmalloc_get_private_dirty() 判斷實際記憶體壓力,避免 RSS 誤導 (含共享庫、頁面快取)。
  • 高併發場景務必呼叫 zmalloc_enable_thread_safeness() (Redis 啟動時自動啟用)。
  • 自訂 OOM handler 可實作「拒絕寫入指令、回傳 OOM 錯誤」而非直接 abort,提升可用性。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved