chap7記憶體分配

2021-09-30 14:37:56 字數 3899 閱讀 8810

所謂堆是一段長度可變的虛擬記憶體,始於程序的未初始化資料段末尾(bss)。通常將堆的當前記憶體邊界稱為「program break」。

我們依然結合程序記憶體布局看來。

改變堆的大小其實就像命令核心改變程序的program break位置一樣,最初,program break正好位於未初始化資料段末尾之後。在program break的位置抬公升後,程式可以訪問新分配區域內的任何記憶體位址,而此時物理記憶體頁尚未分配。核心會在程序首次試圖訪問這些虛擬記憶體位址時自動分配新的物理記憶體頁。

系統呼叫brk()會將program break設定為引數addr所指定的位置。由於記憶體以頁為單位進行分配,addr實際為下乙個頁的邊界處。

當試圖將program break設定為乙個低於其初初始值的位置時,也就是低於&end位置時,可能會導致無法預知的問題。比如說我們常見的分段記憶體訪問錯誤(segmention fault)

呼叫sbrk()將program break在原有位址上增加incer引數的大小,呼叫成功返回乙個program break的位址,返回的是新增大小的起始位址。(可以想到,你可以呼叫sbrk(0)去跟蹤乙個堆的情況)

對於malloc函式我們並不陌生,學習c語言時學過malloc的用法。

malloc函式在堆上分配size位元組大小的記憶體,返回新記憶體起始處的位址,⚠️所分配的記憶體未經初始化,若無法分配記憶體,malloc返回null。由於malloc返回記憶體塊採用記憶體對齊的方式,在大多數硬體架構上,malloc是基於8或16位元組邊界來分配記憶體。

函式calloc()用於給一組相同物件分配記憶體,引數指定分配物件的數量,size指定每個物件的大小,與malloc不同,calloc會將已分配的記憶體初始為0。

realloc()函式通常用來調整一塊記憶體的大小。引數ptr用來指向調整的記憶體,size指定所需調整的大小,函式返回指向大小調整後記憶體塊的指標。若realloc增加了已分配記憶體塊的大小,它不會對額外分配的記憶體進行初始化。

通常情況下,當增大已分配的記憶體時,realloc()會嘗試合併在空閒列表中緊隨其後且大小滿足要求的記憶體塊,若原記憶體位於堆的頂部,那麼函式將對堆空間進行擴充套件。若這塊記憶體位於堆中部並且緊鄰其後的記憶體空間大小不足,realloc會新分配一塊記憶體,並將原資料複製到新記憶體塊中,總的來說就是realloc()函式能夠移動記憶體,這會大量占用資源,因此,盡量避免使用realloc。

當然,你也可以用realloc()去重新定位一塊記憶體

new = realloc(ptr, newsize);

if(new != null)

free函式釋放ptr所指向的記憶體塊,一般情況下,free並不降低program break的位置,而是將這塊記憶體新增到空閒記憶體列表中供後續使用。原因如下:

1. 被釋放的記憶體一般位於堆的中間而不是頂端。

2. 這樣會減少sbrk系統呼叫的使用次數

3. 降低program break的收益不大,因為對於分配大量記憶體的程式來說,它們通常傾向於持有已分配記憶體或者是反覆釋放和重新分配記憶體,而不是釋放所有記憶體後在持續執行。

我們可以**一下free()對於progeram break的影響。下面有段程式,在分配了多塊記憶體後,根據命令列引數來釋放其中的部分或者全部。程式前兩個命令列引數指定分配記憶體塊的數量,大小,第三個引數表示釋放記憶體的迴圈步長,有兩個狀態,為1釋放每塊記憶體,為2隔一塊釋放一塊記憶體,第4、5個引數設定需要釋放記憶體塊的範圍。步長預設設定1。

#include 

#include

#include

#define max_allocs 1000000

#define gn_gt_0 2

#define gn_any_base 0100

#define gn_base_8 0200

#define gn_base_16 0400

static

long getnum(const

char *fname, const

char *arg, int flags, const

char* name)

base = (flags & gn_any_base) ? 0 : (flags & gn_base_8) ? :(flags & gn_base_16) ? 16 : 10;

res = strtol(arg, &endptr, base);

return res;

}int getint(const

char *arg, int flags, const

char *name)

return (int)res;

}//分配記憶體塊的數量,大小,釋放記憶體的迴圈步長,需要釋放記憶體塊的範圍

首先我分配1000個記憶體塊,然後每隔一塊記憶體釋放一塊記憶體。在釋放所有記憶體塊後,堆頂仍然為malloc後的大小。

然後嘗試釋放部分記憶體塊的情況,只留最後一塊記憶體不釋放,發現堆頂不變。

這裡有出現了乙個問題,根據資料來看,如果在堆頂釋放完整的一組連續記憶體塊的話,free會呼叫sbrk使堆頂降下來,而我執行現實並沒有任何變化。

這種情況下,free函式的glibc實現會在釋放記憶體時將相鄰的空間記憶體合併為一整塊更大的記憶體,因而也有能力辨識出堆頂的整個空閒區域。所以實際情況下堆頂應該是降低的。

alloca()通過增加棧幀的大小從堆疊上分配記憶體。根據定義,當前呼叫函式的棧幀位於堆疊的頂部,幀的上方存在擴充套件空間,只需修改堆疊指標值就行。引數size指定在堆疊上分配的位元組數,函式返回指向已分配記憶體塊的指標。

要⚠️ 的是,不能在函式的引數列表中呼叫alloca(),會使alloca分配的堆疊空間出現在當前函式引數的空間內,而函式引數是有固定的位置的。

alloca()函式相對malloc來說具有一定的優勢,編譯器將alloca作為內聯**處理,並通過直接調整堆疊指標來實現,此外,alloca也不需要維護空閒記憶體塊列表。另外,alloca()函式分配的記憶體隨棧幀的移除而自動釋放,也就是呼叫呼叫alloca的函式返回則釋放。

stm32h7記憶體分配 談談H7的記憶體分配以及總結

首先呢,大家在使用h743 的時候,在keil或者iar中,有乙個記憶體的勾選,如圖1,irom1表示flash的記憶體位址,h743是2m,所以,大小自然是2m,起始位址可以在參考手冊中找到,如圖2,這個比較容易理解,程式就是從這開始執行。其次,是ram,743內部有1m,那麼這1m怎麼分的呢,直...

收藏 C Tips 7 堆記憶體分配

這一系列文章 c tips 是公司code committee專家會推薦工程師看的,感覺很好,拿出來與大家共同提高。並不是知道多少會使人與人產生差別,真正的差別在於你能做到多少。coofucoo 在堆上分配記憶體 可能許多人對記憶體分配上的 棧stack 和 堆heap 還不是很明白。包括一些科班出...

記憶體分配 Go記憶體管理 記憶體分配一

go作為乙個比較新晚 新 的語言,自然借鑑前輩們的優點,比如說語言本身負責記憶體管理 對協程和高併發的高優支援 簡單高效的語法等。本篇及後續的幾篇要講的就是還沒提到的比較複雜的記憶體管理。學習記憶體管理 分配 前,如果有jvm的記憶體管理的基礎,會變得非常簡單,如果是第一次接觸記憶體管理,在看完go...