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

free() 如何釋放不同大小記憶體區塊:分配器內部機制揭秘

     編輯:C++ 入門

free() 如何釋放不同大小記憶體區塊:分配器內部機制揭秘

許多開發者好奇:free(ptr) 只接收指針,不接收大小,為何能正確釋放對應大小的記憶體?答案在於分配器在分配時,會在用戶指針前方預留元數據區塊,記錄塊大小等信息。

MSVC 分配器實現分析

在 malloc.h 中定義 _ALLOCA_S_MARKER_SIZE:x86 為 8 字節,x64 為 16 字節。分配時:

void *_MarkAllocaS(void *_Ptr, unsigned int _Marker) {
    if (_Ptr) {
        *((unsigned int*)_Ptr) = _Marker;  // 寫入大小標記
        _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE; // 偏移返回用戶指針
    }
    return _Ptr;
}

釋放時:_freea 先向前偏移 _ALLOCA_S_MARKER_SIZE 找回原始指針,讀取大小標記,再調用底層 free。

SGI STL debug_alloc 實現

static void* allocate(size_t __n) {
    char* __result = (char*)_Alloc::allocate(__n + 8); // 多分配 8 字節
    *(size_t*)__result = __n; // 前 4/8 字節存大小
    return __result + 8; // 返回偏移後指針
}
static void deallocate(void* __p, size_t __n) {
    char* __real_p = (char*)__p - 8; // 找回原始指針
    assert(*(size_t*)__real_p == __n); // 校驗大小一致
    _Alloc::deallocate(__real_p, __n + 8);
}

GCC/glibc 類似設計

同樣採用「前置元數據 + 指針偏移」模式。_ALLOCA_S_MARKER_SIZE 根據架構定義為 8 或 16 字節。

驗證實驗

int main() {
    void *p = NULL;
    srand(time(0));
    for (int a = 10; a--; ) {
        int n = rand() % 10000;
        p = malloc(n);
        size_t w = *((size_t*)((char*)p - 16)); // x64 下前 16 字節
        cout << "w=" << w << " n=" << n << endl;
        assert(w == n);
        free(p);
    }
}

實驗證明:用戶指針前固定偏移處確實存儲了分配大小。

核心結論

  • malloc 返回的指針 = 原始塊指針 + 元數據大小
  • free 時向前偏移讀取元數據,得知真實塊大小
  • 這也是為何 free(ptr+1) 會崩潰:偏移後不再指向元數據區
  • new[]/delete[] 同理,編譯器在塊頭記錄陣列元素個數以正確調用析構函數

閱讀分配器源碼是理解記憶體管理的最佳途徑,建議開發者定期研讀開源實現。

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