Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學

Linux malloc 記憶體分配深度剖析:從用戶空間到內核空間的完整映射鏈路

編輯:Linux 系統常見問題解答

背景知識:進程虛擬地址空間佈局

用戶空間(3GB on x86_64)自低向高:


+------------------+ 0x00007fffffffffff (用戶空間上限)
|    Stack         | ↓ 向下增長
|    (main thread) |
|------------------|
|    ...           |
|    Shared Libs   | (ld.so, libc, etc.)
|------------------|
|    Heap          | ↑ 向上增長 (brk)
|------------------|
|    BSS           |
|    Data          |
|    Text (Code)   |
+------------------+ 0x00000000400000 (典型基址)

關鍵結構體 mm_struct (include/linux/mm_types.h)

struct mm_struct {
    struct vm_area_struct *mmap;      // VMA 鏈表/紅黑樹
    struct rb_root mm_rb;             // VMA 紅黑樹根
    pgd_t *pgd;                       // 頁全局目錄基址
    atomic_t mm_users;                // 用戶空間引用計數
    atomic_t mm_count;                // 結構體引用計數
    int map_count;                    // VMA 數量
    unsigned long start_code, end_code;
    unsigned long start_data, end_data;
    unsigned long start_brk, brk, start_stack;  // 堆/棧邊界
    // ...
};
  • start_brk:堆起始地址(靜態數據段結束處)
  • brk:堆當前頂端地址(動態變化)
  • mmap:記錄所有已映射區域(VMA)

地址映射機制:虛擬地址 → 物理地址

  1. CPU 發出虛擬地址
  2. MMU 查頁表:PGD → PUD → PMD → PTE
  3. PTE 包含物理頁框號 (PFN) + 標誌位
  4. 物理地址 = PFN << PAGE_SHIFT | 頁內偏移
  5. TLB 命中則跳過頁表走查

缺頁中斷時,內核分配物理頁、建立頁表映射、更新 VMA。

malloc/free 實現層次

用戶態:glibc malloc (ptmalloc2)

  • 維護 free chunk 雙向鏈表(fastbin、smallbin、largebin、unsorted bin)
  • Chunk 結構:
struct malloc_chunk {
    size_t prev_size;  // 前一塊大小(若空閒)
    size_t size;       // 當前塊大小 | 標誌位
    struct malloc_chunk *fd;  // 雙向鏈表指針
    struct malloc_chunk *bk;
    struct malloc_chunk *fd_nextsize; // large bin 用
    struct malloc_chunk *bk_nextsize;
};
  • 分配策略:首次適配 / 最佳適配
  • 無法滿足時 → 調用 brk() 或 mmap() 擴展堆
  • 返回地址 = chunk 位址 + sizeof(chunk header)

內核態:系統調用

系統調用用途觸發條件
brk(addr)移動堆頂端小塊分配(< 128KB,可調)
mmap()匿名映射建立新 VMA大塊分配、非連續需求、M_MMAP_THRESHOLD
munmap()釋放映射區域free 大塊內存

malloc 分配流程簡化偽代碼

void *malloc(size_t size) {
    // 1. 對齊、最小塊處理
    size = align(size + SIZE_SZ);

    // 2. 掃描 fastbin / smallbin
    if (size <= MAX_FAST) {
        chunk = fastbin_get(size);
        if (chunk) return chunk2mem(chunk);
    }

    // 3. 掃描 smallbin / largebin / unsorted bin
    chunk = bin_search(size);
    if (chunk) return chunk2mem(chunk);

    // 4. 向系統申請記憶體
    if (size < MMAP_THRESHOLD) {
        // 擴展堆
        new_brk = sys_brk(current_brk + size + overhead);
        chunk = create_chunk_at(old_brk, size);
    } else {
        // mmap 映射
        chunk = sys_mmap(NULL, size, PROT_READ|PROT_WRITE,
                         MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    }

    // 5. 加入 bin、分割、返回
    return chunk2mem(chunk);
}

free 釋放流程

  1. 檢查指針有效性、對齊
  2. 獲取 chunk header
  3. 合併相鄰空閒 chunk(向前/向後)
  4. 放入對應 bin
  5. 若為 mmap 分配且大於閾值 → munmap() 歸還內核
  6. 若堆頂端有大量空閒 → brk() 縮減堆

關鍵調優參數

環境變數默認值作用
MALLOC_ARENA_MAXCPU核數×8限制 arena 數量,減少鎖爭用
M_MMAP_THRESHOLD128KB大於此值用 mmap
M_TRIM_THRESHOLD128KB堆頂空閒超此值歸還內核
M_TOP_PAD0擴展堆時額外預留

調用:mallopt(M_MMAP_THRESHOLD, 256*1024) 或環境變數預設。

常見問題與診斷

  • 記憶體洩漏:valgrind --leak-check=full、mtrace()
  • 堆損壞:malloc_check_=3、glibc 內建檢查
  • 碎片化:觀察 /proc/PID/smaps、mallinfo()
  • 效能瓶頸:perf record -g -- malloc、調整 arena 數

進階:jemalloc / tcmalloc 替代方案

  • 多線程下鎖爭用更低
  • 更積極的記憶體歸還
  • 豐富的統計/分析工具(jeprof, pprof)
  • 預載入替換:LD_PRELOAD=/usr/lib/libjemalloc.so
Copyright © Windows教程網 All Rights Reserved