《程序員的自我修養》--內存

內存佈局

一般有如下佈局幾個區:
:維護函數調用上下文,離開棧,函數調用沒辦法實現。
:容納應用程序動態分配的內存區域。
可執行文件映像:可執行文件在內存裏的映像。
保留區:隊內存中受到保護,禁止訪問的內存區域總稱。
一個進程裏典型的內存佈局如下:
這裏寫圖片描述

棧總是向下增長。棧頂由esp寄存器進行定位,壓棧使得棧頂減小,出棧使得棧頂增加。單純減小esp值等於在棧上開闢空間,單純增加esp值等於在棧上回收空間。

堆棧針:保存函數調用所需要的維護信息,包括:
1.函數的返回值和參數。
2.臨時變量:函數非靜態局部變量和編譯器自動生成其他臨時變量。
3.保存上下文:包括函數調用前後需要保持不變的寄存器。

esp和ebp
esp:指向棧頂,指向當前函數的活動記錄頂部。
ebp:指向函數活動記錄的一個固定位置。

調用慣例
調用慣例:確保函數能夠被正常調用。包括:
1.函數參數的傳遞順序和方式。最常見的是通過棧傳遞。函數調用方將參數壓入棧,函數自己把參數從棧裏取出來,有一些調用慣例允許使用寄存器傳遞參數。
2.棧的維護方式。數據出棧的工作可以由函數的調用方式來完成,也可以由函數本身完成。
3.名字修飾的策略。鏈接的時候對調用慣例進行區分,調用慣例要對函數本身的名字進行修飾,不同的調用慣例有不同的修飾名字。
C 語言默認的調用慣例是cdecl,它傳參時,從右到左的順序把參數壓入棧,由函數調用本身完成出棧,在函數名稱前面加一個下劃線作爲名字修飾。

堆與內存管理

出現堆的原因:棧上的數據在函數返回時會被釋放,無法將數據傳遞到函數外部,全局變量沒有辦法動態地產生,因此出現堆。
linux進程堆管理
兩種堆空間的分配方式:
brk();
mmap();
brk()設置進程數據段的結束地址,它可以擴大或者縮小數據段,如果把數據段的結束地址向高地址移動,,那麼擴大的部分作爲堆是常見做法。
mmap()向操作系統申請一段虛擬地址空間,當這段空間不用來映射某個文件的時候,稱這塊空間是匿名空間。可以用來作爲堆。

void *mmap(
    void *start,//申請的起始空間
    size_t length,//申請的長度
    int prot,//設置申請的空間權限
    int flags,//設置申請的映射類型
    int fd,//文件映射時指定文件描述符
    off_t offset//文件映射時指定文件偏移
);
mmap所申請的起始空間和申請長度必須是系統頁的整數倍。

堆分配算法
1.空閒鏈表:那堆中各個空閒的塊按照鏈表的方式連接起來,當用戶請求一塊空間時,可以遍歷整個鏈表,找到合適大小的塊並把他們拆分,當用戶釋放空間的時候把他們合併到空閒鏈表中。
分配方式:
把空閒塊分成兩部分,一部分作爲程序請求的空間,另一部分爲剩下來的剩餘空間,把空閒塊更新爲剩餘空間,如果剩餘塊是0,那麼直接把它從空閒塊的鏈表中刪除。
缺點:一旦鏈表被破壞或者記錄長度的int被破壞,整個堆就無法正常工作。
2位圖:整個堆劃分爲大量的塊,每一塊大小相同。當用戶請求時,總是分配整數個塊給用戶,可以用一組整數數組來記錄塊的使用情況。由於每一個塊只有頭/主體/空閒三種狀態,因此只需要兩位便能表示一個塊。
優點:速度快,穩定性好,塊不需要額外信息容易管理。
缺點:容易產生碎片,位圖如果很大,查找命中率低。
3對象池:被分配的對象是幾個較爲固定的值。如果每一次分配的空間大小都一樣,那麼可以把每次請求分配的大小作爲一個單位,把整個堆空間劃分爲大量小塊,每次請求時候找到一個小塊就可以了。對於它的管理,空閒鏈表和位圖都可以。

發佈了35 篇原創文章 · 獲贊 14 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章