簡體   English   中英

為什么Linux在分配動態內存時會占用一些額外的空間?

[英]Why does linux take some extra space when allocating dynamic memory?

我正在一個需要對大量字符串值進行快速字符串搜索的項目中。 我決定使用Trie進行搜索,這種方法很快。 這是該項目的一部分,與我的問題有關:

class TTrieNode{
public:
    char c;
    bool data;
    TTrieNode *left, *mid, *right;  
    TTrieNode(){
        left = mid = right = NULL;
        c = data = 0;
    }
};

class TTrie{
private:
    TTrieNode *root;
    TTrieNode *insert(TTrieNode*n, char *s, int idx){
        char ch = s[idx];
        if(!n){
            n = new TTrieNode();
            n->c = ch;
        }
        if(ch < (n->c)){
            n->left = insert(n->left, s, idx);
        }else if(ch > (n->c)){
            n->right = insert(n->right, s, idx);
        }else if(idx+1 < strlen(s))
            n->mid = insert(n->mid, s, idx+1);
        else
            n->data = true;
        return n;
    }
public:
    TTrie() {
        root = NULL;
    }
    void insert(char *s) {
        root = insert(root, s, 0);
    }
};

一切都很好,直到我們在真實數據上測試了Trie。 根據我對節點數和每個節點占用的空間量的計算,它應該占用了約40GB的RAM,但令我驚訝的是,它占用了約70GB。 起初,我認為這是因為每個節點的內存對齊(只是一個原始的猜測!),所以我在TTrieNode定義中使用了__attribute__((packed, aligned(1))) 使用這個並沒有什么大的不同。 經過大量測試后,我使用了一些手動內存分配方法。 因此,我不需要在每次要向新節點分配內存時調用new而是在程序的開頭分配了約50GB的RAM,並使用以下自定義新函數:

TTrieNode *memLoc;
int memIdx;
void initMemory(){
    memLoc = (TTrieNode*) malloc(MAXNODES * sizeof(TTrieNode));
    memIdx = 0;
}
TTrieNode*myNew(){
    memLoc[memIdx].left =  memLoc[memIdx].right =  memLoc[memIdx].mid = NULL;
    memLoc[memIdx].c =  memLoc[memIdx].data = 0;
    return &memLoc[memIdx ++];
}

這非常令人驚訝,但是這次,該程序完全占用了我期望的內存量!

現在我的問題是:

為什么每個new (malloc)一些額外的內存? 內核/用戶級別是否為每種內存分配提供某種指針? 我尚未在Windows(或任何其他操作系統)中測試我的代碼,但想知道在這些操作系統上是否也有類似的行為。

每個分配的塊有8到16字節的開銷。 在典型的x86_64分配器中,需要8個字節的開銷才能在釋放內存塊時正確地組織它們。 還有一個16字節對齊要求,因此,已經是16字節倍數的塊獲得基本8字節開銷需要浪費另外8字節。

典型的64位設計:每個塊之前都有一個8字節的控制字。 需要大多數控制字來指定該塊的大小,因此可以釋放它。 底部的幾位可用於其他目的,因為大小可以被16整除。其中最重要的目的是知道前面的塊是否空閑。 釋放此塊后,如果先前的塊已經釋放,則將其合並。 如果可能的話,它還會與下一塊合並。 但是能夠做到這一點並不需要額外的信息。

常見的最小信息令人驚訝(優雅),尤其是每個塊頭都必須包含一點信息來說明先前的塊是否空閑,而無需花費一點時間來說明當前塊是否空閑。 對於合並,您可以找到下一個塊,因為您知道該塊的大小。 但是只有很少的信息,您才能找到以前的塊,除非您已經知道它是免費的,但是除非它是免費的,否則您不需要找到它。 因此,在空閑塊的末尾有一個指向其開始的指針(或等效大小)。 因此,如果它是免費的,則可以從其后繼產品導航到它。 但是,如果不是免費的,那將是使用數據的一部分,而不是開銷。 您可以通過轉到繼任者的繼任者並查看其前任是否空閑來了解繼任者是否自由。 這比使用更多的備用位更為優雅,但不一定更好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM