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

線性內存池設計與實現:基於位圖的定長塊分配器,解決碎片與洩漏的工程實踐

     編輯:關於C語言

前言

頻繁 malloc/free 導致內存碎片、分配開銷大、洩漏難查、多線程鎖競爭。本文設計一個定長塊內存池,採用位圖管理空閒塊,實現 O(1) 分配釋放、零碎片、內建洩漏統計、邊界保護,適合嵌入式、高頻分配、實時系統場景。

一、數據結構設計


#include 
#include 
#include 

typedef int Status;
#define TRUE 1
#define FALSE 0

// 內存池控制塊
typedef struct {
    void *pool_mem;      // 大塊內存起始地址 (含數據區 + 位圖區)
    char *bitmap;        // 位圖指針 (指向 pool_mem 後的位圖區)
    void *data_area;     // 數據區起始地址 (對齊後)
    size_t block_size;   // 单塊大小 (含用戶數據 + 可選頭部)
    size_t block_count;  // 總塊數
    size_t free_count;   // 空閒塊數 (快速統計)
    size_t align;        // 對齊要求
} MemPool;

二、初始化:計算佈局、對齊、位圖大小


MemPool* mempool_create(size_t block_size, size_t block_count, size_t align) {
    if (block_size == 0 || block_count == 0) return NULL;
    align = (align == 0) ? sizeof(void*) : align; // 默認指針對齊
    
    // 1. 計算位圖大小 (每塊 1 bit,向上取整到字節)
    size_t bitmap_bytes = (block_count + 7) / 8;
    
    // 2. 計算總內存大小:控制塊 + 數據區(對齊) + 位圖區
    // 數據區起始需對齊
    size_t data_offset = (sizeof(MemPool) + align - 1) & ~(align - 1);
    size_t total_size = data_offset + block_count * block_size + bitmap_bytes;
    
    // 3. 一次性分配
    void *mem = malloc(total_size);
    if (!mem) return NULL;
    memset(mem, 0, total_size);
    
    // 4. 初始化控制塊 (放在最開頭)
    MemPool *pool = (MemPool*)mem;
    pool->block_size = block_size;
    pool->block_count = block_count;
    pool->free_count = block_count;
    pool->align = align;
    
    // 5. 設置區域指針
    pool->data_area = (char*)mem + data_offset;
    pool->bitmap = (char*)mem + data_offset + block_count * block_size;
    
    return pool;
}

三、分配:掃描位圖尋找首個 0 位 (First-Fit)


void* mempool_alloc(MemPool *pool) {
    if (!pool || pool->free_count == 0) return NULL;
    
    // 掃描位圖字節
    for (size_t byte_idx = 0; byte_idx < (pool->block_count + 7) / 8; byte_idx++) {
        unsigned char byte = pool->bitmap[byte_idx];
        if (byte == 0xFF) continue; // 該字節 8 塊全滿
        
        // 逐位檢查
        for (int bit = 0; bit < 8; bit++) {
            size_t block_idx = byte_idx * 8 + bit;
            if (block_idx >= pool->block_count) break;
            if ((byte & (1 << bit)) == 0) { // 找到空閒
                // 標記佔用
                pool->bitmap[byte_idx] |= (1 << bit);
                pool->free_count--;
                return (char*)pool->data_area + block_idx * pool->block_size;
            }
        }
    }
    return NULL; // 理論上不會到達
}

四、釋放:地址合法性校驗 + 位圖清零


Status mempool_free(MemPool *pool, void *ptr) {
    if (!pool || !ptr) return FALSE;
    
    // 1. 地址範圍檢查
    char *start = (char*)pool->data_area;
    char *end = start + pool->block_count * pool->block_size;
    char *p = (char*)ptr;
    if (p < start || p >= end) return FALSE; // 非本池指針
    
    // 2. 對齊檢查 (必須是塊起始地址)
    size_t offset = p - start;
    if (offset % pool->block_size != 0) return FALSE; // 非法指針 (內部指針)
    
    size_t block_idx = offset / pool->block_size;
    
    // 3. 重複釋放檢查 (位圖已為 0)
    size_t byte_idx = block_idx / 8;
    int bit = block_idx % 8;
    if ((pool->bitmap[byte_idx] & (1 << bit)) == 0) return FALSE; // 雙重釋放
    
    // 4. 標記空閒
    pool->bitmap[byte_idx] &= ~(1 << bit);
    pool->free_count++;
    
    // 可選:清零數據區 (安全/調試模式)
    // memset(p, 0, pool->block_size);
    
    return TRUE;
}

五、銷毀與統計


void mempool_destroy(MemPool *pool) {
    if (pool) free(pool); // 一次性釋放整個大塊
}

size_t mempool_free_count(const MemPool *pool) {
    return pool ? pool->free_count : 0;
}

size_t mempool_used_count(const MemPool *pool) {
    return pool ? pool->block_count - pool->free_count : 0;
}

六、進階擴展方向 (生產級增強)

特性實現要點
變長塊分配多級池 (如 16/32/64/128/256/512/1024/2048 Bytes),請求按尺寸路由到最小滿足池
最佳適配維護空閒塊鏈表按地址排序,分配時尋找最小滿足塊 (減碎片),釋放時合併相鄰空閒塊
線程安全每池一把自旋鎖 / 無鎖環形隊列 (單生產單消費) / Thread-Local Cache (TCMalloc 風格)
調試模式塊頭添加魔數 0xDEADBEEF、分配棧跡、紅區、雙重釋放檢測、洩漏報告 (析構時 free_count != block_count 報警)
內存對齊保證posix_memalign 分配大塊,data_area 按 max_align_t 對齊,滿足 SIMD/原子操作要求
統計監控峰值使用量、分配/釋放次數、平均塊壽命、碎片率導出 Prometheus 指標

七、測試驗證矩陣

測試描述預期
基本分配釋放循環 alloc/free 10000 次無洩漏、free_count 歸零
滿池壓力分配至 free_count=0,再 alloc 失敗返回 NULL、不崩潰
雙重釋放free 同一指針兩次返回 FALSE、不崩潰
非法指針free 堆指針、棧指針、池內偏移指針返回 FALSE
併發壓力多線程並發 alloc/free (加鎖版)無數據競爭、計數一致
對齊驗證分配指針 % align == 0全通過

結語

基於位圖的定長塊內存池,以「一次大分配、位圖 O(1) 管理、零碎片、內建統計」的極簡架構,解決了高頻小塊分配的核心痛點。理解其佈局計算、位圖掃描、地址合法性驗證三大核心邏輯,即可靈活擴展為多級池、無鎖池、調試池,成為系統級內存管理的基石組件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved