首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

解析 malloc 記憶體對齊策略:為什麼 new 1 Byte 和 24 Byte 佔用相同記憶體?

     編輯:關於C++

現象觀察:記憶體分配的「階梯效應」

測試環境:Linux 64-bit, GCC 編譯。循環 `new char[size]` 100 萬次,觀察 `top` 中 RES (常駐記憶體):

請求大小實際 RES倍數關係
1 Byte32 MB32 × 1M
24 Byte32 MB32 × 1M
25 Byte48 MB48 × 1M

疑問:為何 1~24 Byte 同佔 32 Byte?25 Byte 突然跳到 48 Byte?

1 Byte 分配記憶體佔用

24 Byte 分配記憶體佔用

25 Byte 分配記憶體佔用

根本原因:glibc malloc 內部機制

核心宏定義 (glibc malloc.c)

#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))           // 64-bit 為 8
#define MALLOC_ALIGNMENT (2 * SIZE_SZ)              // 16 bytes (2*指針大小)
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)    // 0xF

struct malloc_chunk {
    INTERNAL_SIZE_T prev_size;  // 前一塊大小 (若空閒)
    INTERNAL_SIZE_T size;       // 當前塊大小 + 標誌位
    struct malloc_chunk* fd;    // 雙向鏈表 (空閒時)
    struct malloc_chunk* bk;
};

#define MIN_CHUNK_SIZE (sizeof(struct malloc_chunk))  // 32 bytes (64-bit)
#define MINSIZE (((MIN_CHUNK_SIZE + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))  // 32

// 核心對齊計算
#define request2size(req) \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
     MINSIZE : \
     ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

64-bit 系統關鍵參數

request2size 計算實例


// req = 1
request2size(1) = (1 + 8 + 15 < 32) ? 32 : ... → 32

// req = 24
request2size(24) = (24 + 8 + 15 < 32) ? 32 : ... → 32  (47 < 32 為假,取後者)
// 實際計算:(24 + 8 + 15) = 47, 47 & ~15 = 47 & 0xFFFFFFF0 = 32

// req = 25
request2size(25) = (25 + 8 + 15 < 32) ? 32 : ... → 48
// (25 + 8 + 15) = 48, 48 & ~15 = 48

實測驗證:相鄰分配地址差值


// 測試代碼
char* p1 = malloc(i);
char* p2 = malloc(1);
printf("i=%d, diff=%ld\n", i, p2 - p1);

32-bit 系統測試結果 (MINSIZE=16, 對齊=8):

i=1~12  → diff=16
i=13~20 → diff=24
i=21~28 → diff=32
i=29~36 → diff=40
... 每 8 bytes 一級

64-bit 系統測試結果 (MINSIZE=32, 對齊=16):

i=1~24   → diff=32
i=25~40  → diff=48
i=41~56  → diff=64
i=57~72  → diff=80
... 每 16 bytes 一級

32-bit 分配間距測試

64-bit 分配間距測試

記憶體佈局視覺化

用戶視角:          malloc 內部 chunk:
┌─────────────┐     ┌────────────────────┐
│  用戶數據     │ ←── │ prev_size (8B)     │  ← chunk 指針
│  (request)   │     │ size | flags (8B)  │
└─────────────┘     ├────────────────────┤  ← 返回給用戶的指針 (mem)
                    │ 用戶數據區          │
                    │ ...                │
                    ├────────────────────┤
                    │ 下一個 chunk header │
                    └────────────────────┘

對 STL 分配器的影響

SGI STL 分配器 (舊版) / GCC `__gnu_cxx::__pool_alloc`:

  • 自由鏈表按 8 bytes 遞增:8, 16, 24, 32, ..., 128
  • `refill` 時一次申請 20 塊 → `chunk_alloc` 申請 20 × size × 2
  • 64-bit 系統:申請 8 bytes × 20 × 2 = 320 bytes → malloc 實際給 336 bytes (浪費 16B)

優化建議:零浪費 chunk_alloc


// 在 chunk_alloc 內部計算實際 malloc 大小
size_t bytes = n * size * 2;  // 原始需求
size_t actual = request2size(bytes);  // 模擬 malloc 對齊
// 調整 n 使 actual 接近 bytes,消除對齊浪費
while (n > 1 && request2size(n * size * 2) > bytes + MALLOC_ALIGNMENT) {
    --n;
}
// 實際申請 n * size * 2,malloc 返回剛好對齊大小

總結

  1. malloc 最小單位 MINSIZE:32-bit 16B、64-bit 32B,非對齊單位
  2. 對齊公式:`(req + SIZE_SZ + ALIGN_MASK) & ~ALIGN_MASK`,向上取整到對齊倍數
  3. 隱形浪費來源:chunk header (16B/32B) + 對齊填充
  4. 工程對策:記憶體池設計時模擬 `request2size`,調整批次大小實現零浪費
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
參數值說明
SIZE_SZ8size_t 大小 = 指針大小
MALLOC_ALIGNMENT16對齊單位 = 2 × 指針大小
MIN_CHUNK_SIZE32chunk header 大小 (prev_size + size + fd + bk)
MINSIZE32最小分配單位 (對齊後)