C 記憶體管理 記憶體池

2021-06-29 06:35:12 字數 3852 閱讀 7835

很多內容來自於網際網路,如有侵權,請告知。

另外,從** 收穫很多,在此表示感謝。

我們寫程式經常需要 malloc 和 new 一塊記憶體出來, 這些記憶體是在堆上進行分配的,在堆上分配的記憶體和在棧上分配的記憶體不同,可以長久的儲存。

堆是什麼: 可以把你的程序空間 想象成 4g 大小的記憶體(32 為機子上): 0~ 4gb。 而堆是 其中間的一塊連續的記憶體區域。 程式設計師知道 這塊記憶體區域的起始位置結束位置,並且這塊連續的記憶體區域可以被程式設計師隨意使用,並且這塊區域是很大的。

推薦文章:what and where are the stack and heap?

堆是一塊 連續的 記憶體區域,是用來儲存資料的。

我們為什麼會需要對堆 進行管理呢?

因為堆是一塊很大的空間。 可以在程式執行過程中隨時使用。 但是在程式執行過程中,當我們需要資料的時候,我們將會從堆上申請記憶體。

那麼問題來了: 當我們想要從堆上申請記憶體的時候,從堆上的**取出需要的記憶體塊呢?

因為有些地方已經可能被別的資料所占用了,我們需要記錄這些資訊,當申請記憶體的時候,可以得到一塊有用的記憶體

如何堆堆進行管理呢?

很簡單,我們只需要兩張表就可以了。 第一張表記錄了哪些記憶體塊被使用了,稱為alloc_list。 第二張表記錄了哪些記憶體塊未被使用,稱為 free_list。 當需要申請記憶體的時候,從第二張表中查詢 找到滿足要求的記憶體塊,並且將此記憶體塊 從 free_list 中刪除,加入到 alloc_list中。釋放的時候同樣如此。

這種管理記憶體的方式有什麼缺點呢?

因為我們申請的記憶體塊 大小 不定,申請和釋放時間也不相同。 所以 這個堆 (連續的整個記憶體塊)會形成 零零散散的情況(稱為記憶體碎片)。 並且 查詢這兩張表 需要時間,所以如果申請和釋放堆記憶體比較頻繁的話,會比較浪費時間。

總之,缺點就是時間和空間消耗嚴重

這種管理方式的優點呢?

通用。

因為你可以申請任意大小的記憶體。

那麼為什麼 需要 記憶體池呢?

節省 時間 和空間。

比如一種情形,你現在需要 對某種塊大小固定(比如說是:256位元組)的 物件進行頻繁的申請和釋放。 那麼是否可以預先申請 256kb 大小(或者更大)的記憶體塊,然後分為 1024 個單元。每個單元大小是 256b。 然後當需要申請 這個物件的時候,直接從其中獲取;釋放的時候,並不是真正的free 掉,而是歸還給這個記憶體塊(標記為可用),供下次申請時分配。

綜上所述:記憶體池就是 預先分配好的 一塊大記憶體。 通過程式設計師自身 對 這塊大記憶體的管理,來滿足自身特殊的 記憶體 申請和釋放的需求。 (比如,申請的記憶體塊總是 256b 這個大小)

記憶體池的優點是什麼:

可以極大的加快記憶體 申請 和釋放的過程。達到 o(1) 級別。

可以在一定程度上減少記憶體鎖片的產生。

記憶體池的缺點是什麼:

不夠通用, 只能分配特定長度的位元組,不是通用的記憶體管理器。

記憶體池可能導致記憶體占用只增加,不減少。

經典的記憶體池:就是分配一塊大空間,然後將這塊記憶體分成大小相同的小物件!這個技術可以極大的加快記憶體分配和釋放的過程。 我們現在寫**分析一下:

參考文章

class mempool;  //整個結構體的大小,就是 每個 item 的大小.

struct _memblock ; // 表示,分配的空間的大小。

_memblock *m_pmemblockheader; //串起整個空閒鍊錶

-freenode *m_pfreenodeheader;

public:

mempool(int nitemsize, int nmemblocksize = 2048) :

m_nitemsize(nitemsize), m_nmemblocksize(nmemblocksize),

m_pmemblockheader(null), m_pfreenodeheader(null)

void* malloc(); //沒有引數,因為分配的大小是固定的

void

free(void *p) //**記憶體。

};void* mempool::malloc()

void *pfreenode = m_pfreenodeheader; //直接從空閒鍊錶上摘下來乙個item大小的空間

m_pfreenodeheader = m_pfreenodeheader->pprev;

return pfreenode;

}void mempool::free(void* p)

上述**十分簡單,照著手敲一遍,自然明白其中的內涵。

這個可以成為最簡單的記憶體池版本了,針對上述記憶體池的實現的一些缺陷,我們現在進行公升級。

boost::pool 也是乙個記憶體池,是boost 庫的乙個記憶體池實現,但是具有以下的改進。

1. 首先我們可以看到上述**是有缺陷的。 因為它 首先分配的一塊 大空間 是定死的。 呼叫mempool::malloc()多次,可能導致 這個大空間耗完,然後沒有空間,可以分配,但是實際上還是存在可用的記憶體的。 boost::pool 是採用動態記憶體分配的,初次設定大空間的 大小為:32*m_nitemsize,當空間不夠用的時候,動態分配空間m_nitemsize*64,第三次申請空間時,分配的大小為m_nitemsize*128

2. 其次,boost:pool 實現了ordered_free(viod *p)函式,這個函式 在釋放空間的時候,不會講這個節點直接掛載在 空閒列表的表頭。 它認為 空閒鍊錶是有序鍊錶,所以插入這個要釋放的節點的時候,會進行查詢,找到合適的位置,然後插入這個節點。 於是我們知道ordered_free的時間複雜度是o(n),而free()的時間複雜度是o(1)(但是不要認為這個是多此一舉哦)

這裡的第一點,在很多地方都有用。屬於使用者需求**。 在 std::vector() 實現中的記憶體增長就使用了這個模型:如果現在的 vector() 記憶體不夠了,那麼重新分配記憶體原來的二倍。 (如果現在vector多餘的空間比較多, 比如說,當使用的空間為整個空間的 1/4 的時候,整個空間縮小為原來的1/2。想一想為什麼?

sgi stl 是 sgi 公司實現的一套 stl 模板庫。 其中的記憶體池技術十分精妙:實現了「基於記憶體池技術的通用記憶體分配元件」

具體思想如下:

遺憾的是, mempool 技術可能導致記憶體占用只會增加不會減少。(比如,當我們申請了很多空間,然後釋放掉這些空間,接下來程式申請次數變得很少,就會導致 記憶體之中占有大量的記憶體沒有釋放。) 現在還沒有非常有效的辦法避免這個情況。

不過貌似乙個作者實現的 scopealloc 中的 blockpool可以避免這個情況

文章推薦: c++中多執行緒 與 singleton(單例模式)那些事,寫的很全面

ibm developer 很推薦,是一本書上的內容

記憶體管理內幕

C 記憶體管理 記憶體池

引子 一 單獨類記憶體池 classa intget void operator new size t void operator delete void size t private a next static a freestore 指向可用首位址 static const int achunk...

C語言記憶體管理(記憶體池)

c語言可以使用 alloc 從棧上動態分配記憶體。malloc free或者 new delete 大量使用會造成記憶體碎片,這種碎片形成的機理如下 記憶體碎片一般是由於空閒的記憶體空間比要連續申請的空間小,導致這些小記憶體塊不能被充分的利用,舉個例子 如果有100 個單位的連續空閒記憶體,那麼先申...

記憶體池 C 記憶體池

c c 下記憶體管理是讓幾乎每乙個程式設計師頭疼的問題,分配足夠的記憶體 追蹤記憶體的分配 在不需要的時候釋放記憶體 這個任務相當複雜。1.呼叫malloc new,系統需要根據 最先匹配 最優匹配 或其他演算法在記憶體空閒塊表中查詢一塊空閒記憶體,呼叫free delete,系統可能需要合併空閒記...