C++ malloc淺析

https://www.cnblogs.com/zpcoding/p/10808969.html

底層實現與兩個系統調用

本篇講講malloc底層是如何實現的。

在32位系統裏,進程地址空間分爲以下幾塊:代碼段(.text)、數據段(.data)、堆、棧、內核空間(3G-4G)。
其中堆和棧中間空着一塊區域,在使用malloc和free時,就是在這片空間上進行操作
在這裏插入圖片描述
malloc可能使用兩種系統調用,當malloc(size)裏的size小於128k(可由M_MMAP_THRESHOLD選項調節)時,使用brk調用,size大於128k時,使用mmap調用

缺頁中斷

當一個進程發生缺頁中斷的時候,進程會陷入核心態,執行以下操作:

1)檢查要訪問的虛擬地址是否合法

2)查找/分配一個物理頁

3)填充物理頁內容(讀取磁盤,或者直接置0,或者什麼都不做)

4)建立映射關係(虛擬地址到物理地址的映射關係)

5)重複執行發生缺頁中斷的那條指令

可以看到,在未讀寫數據時,是不分配物理內存的!懶惰就是力量!

1、size小於128k,使用brk

在這裏插入圖片描述
我們通過控制堆頂指針(_edata)實現brk調用的虛擬內存分配。但不對應物理內存(因此沒有初始化),第一次讀/寫數據時,引起內核缺頁中斷,內核才分配對應的物理內存,然後虛擬地址空間建立映射關係

brk引起的碎片問題

brk實際上只是用一個指針,標誌了可能分配了虛擬內存的高位地址(在_edata以下可能有分配虛擬內存)。brk分配的內存需要等到高地址內存釋放以後才能釋放(例如,在B釋放之前,A是不可能釋放的,因爲只有一個_edata 指針,這就是內存碎片產生的原因)
顯而易見的一個問題就是,一個指針沒辦法標誌其中的空閒內存、已分配內存。如果我們連續只分配而不釋放,很快就會產生小內存碎片。mmap分配的內存可以單獨釋放。

那麼問題來了,什麼時候釋放?
當最高地址空間的空閒內存超過128K(可由M_TRIM_THRESHOLD選項調節)時,執行內存緊縮操作(trim)。在上一個步驟free的時候,發現最高地址空閒內存超過128K,於是內存緊縮。詳細例子見後文,先來講講mmap。

2、size大於128k,使用mmap

在這裏插入圖片描述
圖4是調用malloc(200K)的結果,底層使用mmap分配了空間C,圖5是調用malloc(100K)的結果,底層使用brk分配了空間D,free(C)可以輕鬆釋放虛擬內存和物理內存mmap分配的內存真的很好釋放!
B是brk調用分配的內存空間,free(B)會發生什麼呢?
在這裏插入圖片描述
free(B)不會改變_edata的位置,也不會釋放物理內存和虛擬內存,在此基礎上調用free(D)卻可以完成我們想完成的事情!這是因爲free內部執行了內存緊縮操作(trim)!這裏的內存緊縮僅發生在堆頂端的空閒空間大於128K時(可由M_TRIM_THRESHOLD選項調節)。

malloc失敗

首先,瞭解一下內存分配的機制。
OS將空閒內存塊的地址鏈接爲一個鏈表,方便用戶在申請內存時可以快速定位到空閒內存塊。由於鏈表的特性,空閒內存鏈表的插入對應舊內存塊釋放,刪除對應新內存塊申請,效率很高。
但是並不是每次malloc都能成功,原因可能有:
1、申請的內存超過了當前空閒內存的大小
(如果由於外部碎片而沒有一整塊內存可以滿足申請的內存大小,OS是否會先進行碎片整理?未知…)
2、存在內存越界訪問,破壞了malloc對應內存的信息
爲了對malloc失敗的情況進行錯誤處理,需要注意代碼的健壯性:

if (A=(DATA *)malloc(sizeof(DATA))) == NULL)
		goto err;
...
err:
//錯誤處理代碼

這裏可以用do…while(0)替代goto
關於do…while(0)可以參考https://blog.csdn.net/jojozym/article/details/104847877
計算機真是太妙了!

malloc與new辨析

1、性質不同
malloc和free是庫函數,new和delete是運算符。

2、使用方法不同
malloc的作用是找到一塊空閒內存,並且返回該內存的起始地址,類型是(void*),因此使用上是這樣的形式:

int *a=(int *)malloc(sizeof(int)*len);//len是int數組的長度

如上所示,必須進行強制轉換(int *)

int *a=new int[len];

而new後跟着int,已經指定了數據類型,所以不需要強制轉換。

3、發生錯誤時的處理不同
new會拋出異常,malloc返回NULL表示開闢內存失敗

4、釋放形式在某些情形不同
malloc開闢的空間,使用free逐一釋放。(如果是多維數組,從最內一層開始釋放)
new開闢的空間,如果是用於數組,必須使用delete[]。

5、new底層調用了malloc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章