1.1 對於堆的管理,核心提供了兩個系統呼叫brk和mmap。brk 用於更改堆頂位址,mmap則為程序分配虛擬位址空間。
1.2 當程序向glibc申請記憶體時,如果申請的記憶體的數量大於閥值的時候,glibc會採用mmap為程序分配一塊虛擬位址空間,而不是採用brk來擴充套件棧頂的指標。
1.2.1 上述「閥值」預設情況是128k,這個值可以通過函式來設定:
int mallopt(int param, int value);
param的取值可以為: m_mmap_threshold, m_mmap_max.
l m_mmap_threshold: libc中大塊記憶體的閥值,大於該閥值的記憶體申請,記憶體管理使用mmap系統呼叫申請記憶體;如果小於該閥值的記憶體申請,記憶體管理使用brk系統呼叫來擴充套件堆頂指標。「閥值」預設情況是128k。
l m_mmap_max: 該程序中最多使用mmap分配位址段的數量。
l m_trim_threshold: 堆頂記憶體**閥值,當堆頂連續空閒記憶體數量大於該閥值時, libc的記憶體管理將呼叫系統呼叫brk來調整堆頂位址,釋放空間。該值預設為128k。
l m_top_pad: 當libc記憶體管理器呼叫brk釋放記憶體時,堆頂還需要保留的空閒記憶體數量。該值預設為0。
1.2.2 如果乙個程序頻繁的申請、釋放乙個頁面,必然會導致大量的系統呼叫,從而降低程序的效率。如果記憶體管理在程序程序釋放一塊記憶體時不是返回給系統而是將其cache住,留待下次使用,這樣就可以減少系統呼叫的次數,從而提高程序的效率。它的代價是釋放的記憶體不立刻返還給系統,以記憶體空間換取程序的效率。libc就是採用這種策略,只有堆頂有連續128k(可通過mallopt配置)空閒記憶體時才會呼叫brk通知核心釋放這段記憶體。這就導致下述的記憶體空洞。
1.2.3 如果想小塊記憶體釋放也會觸發堆頂記憶體釋放,有兩種方法: (trim_fastbins 參見:19.4)
1.2.3.1 加上』-dtrim_fastbins=1』,重新編譯libc庫。
1.2.3.2 調整mxfast的值,將其設定為0,使得所有記憶體分配的值都大於mxfast的值。
1.2.4 unmmap用於釋放mmap申請的記憶體,該系統呼叫會立即釋放記憶體。
1.3 記憶體空洞: 只要堆頂還有內存在使用,堆頂下方不管釋放了多少記憶體都不會被釋放。這個是由於linux核心選擇通過調整堆頂來擴充套件和釋放記憶體空間。
1.3.1 啟示:
l 盡可能早的申請長期不釋放的記憶體。
l 只在需要時申請記憶體,記憶體使用完應立即釋放。
1.4 trim_fastbins: 當釋放乙個小塊記憶體時,是否立即對fastbin進行合併;設定為1會進行立即合併,可以減少記憶體消耗,但降低記憶體分配釋放的效率。
trim_fastbins與堆頂記憶體**存在著乙個介面:
在trim_fastbins=0時,當小於或者等於mxfast的小塊記憶體釋放時,並不會觸發堆段頂部記憶體釋放,堆頂記憶體釋放被延遲到大於mxfast的記憶體釋放時觸發,這樣有可能會增加記憶體的碎片。
在trim_fastbins=1時,小於mxfast的小塊記憶體釋放也將觸發堆頂記憶體釋放。
1.5 記憶體碎片:分為內部碎片(已經分配給程序,但是不能被利用的記憶體)和外部碎片(還沒有被分配出去,但是由於太小了而無法分配給申請記憶體的新程序。)。
1.5.1 內部碎片:因為所有的記憶體分配必須起始於可被 4、8 或 16 整除(視處理器體系結構而定)的位址或者因為mmu的分頁機制的限制,決定記憶體分配演算法僅能把預定大小的記憶體塊分配給客戶。假設當某個客戶請求乙個 43 位元組的記憶體塊時,因為沒有適合大小的記憶體,所以它可能會獲得 44位元組、48位元組等稍大一點的位元組,因此由所需大小四捨五入而產生的多餘空間就叫內部碎片。
1.5.2 外部碎片:頻繁的分配與**物理頁面會導致大量的、連續且小的頁面塊夾雜在已分配的頁面中間,就會產生外部碎片。假設有一塊一共有100個單位的連續空閒記憶體空間,範圍是0~99。如果你從中申請一塊記憶體,如10個單位,那麼申請出來的記憶體塊就為0~9區間。這時候你繼續申請一塊記憶體,比如說
5個單位大,第二塊得到的記憶體塊就應該為
10~14
區間。如果你把第一塊記憶體塊釋放,然後再申請一塊大於
10個單位的記憶體塊,比如說
20個單位。因為剛被釋放的記憶體塊不能滿足新的請求,所以只能從
15開始分配出
20個單位的記憶體塊。現在整個記憶體空間的狀態是
0~9空閒,
10~14
被占用,
15~24
被占用,
25~99
空閒。其中
0~9就是乙個記憶體碎片了。如果
10~14
一直被占用,而以後申請的空間都大於
10個單位,那麼
0~9就永遠用不上了,變成外部碎片。
1.5.3 減少記憶體碎片
l 需要考慮記憶體對齊問題
l 採用slaballocation機制:整理記憶體以便重複使用 (
l 盡可能多次反覆使用記憶體塊,而不要每次都對記憶體塊進行分割,以正好符合請求的儲存量。分割記憶體塊會產生大量的小記憶體碎片,猶如一堆散沙。以後很難把這些散沙與其餘記憶體結合起來。比較好的辦法是讓每個記憶體塊中都留有一些未用的位元組。
l 將相鄰空閒記憶體塊連線起來是一種可以顯著減少記憶體碎片的技術。
1.6 堆記憶體從低位址向高位址增長。
1.7 記憶體跟蹤mtrace
1.7.1 libc為記憶體除錯提供了一系列的鉤子函式,見malloc.h。
1.7.2 **中呼叫mtrace();
1.7.3 執行程式前加 #export malloc_trace=mymemory.log
1.7.4 呼叫指令碼(mcheck)分析結果
1.8 常用檢測記憶體洩漏的工具 (
1.8.1 mtrace
1.8.2 dmalloc
1.8.3 valgrind
1.8.3.1 valgrind tools:
1.8.3.1.1 memcheck: 用來檢測程式中出現的記憶體問題,所有對記憶體的讀寫都會被檢測到,一切對
malloc
、free
、new
、delete
的呼叫都會**獲。
l 最好加上-g選項編譯程式。
1.8.3.1.2 callgrind
1.8.3.1.3 cachegrind
1.8.3.1.4 helgrind
1.8.3.1.5 massif
1.8.4 memwatch
1.8.5 mpatrol
1.8.6 dbgmem
堆記憶體管理
堆記憶體是 段當中的其中一段,特點就是大,但不能與識別符號建立聯絡,只能與指標配合使用 c語言沒有提供管理堆記憶體的語句,而是標準庫提供了一套管理記憶體的函式 功能 從堆記憶體中分配記憶體 引數 size 所申請的位元組數,一般使用 sizeof 計算 返回值 所申請的記憶體的首位址 注意 1 如果...
堆與記憶體管理
當程式對堆的操作比較頻繁的時候,使用系統呼叫的方式向核心索要空間的代價太大。比較好的做法是向作業系統申請一塊適當大小的額堆空間,然後又程式自己管理這塊空間,所以管理空間分配的往往時程式的執行庫。執行庫相當於零售商,從核心批發了較大的堆空間,然後零售給程式使用。linux下的程序堆管理提供了兩種堆空間...
堆記憶體和棧記憶體的管理
1 堆記憶體 堆記憶體是由程式設計師手工管理的,但它的申請是需要借助標準庫函式。在大小上,理論上是物理記憶體的大小。關於堆記憶體的資料儲存是靠程式設計師來管理的。由於是由程式設計師管理的,程式設計師的錯誤操作也導致記憶體的洩露和記憶體碎片的問題。關於堆記憶體的標準庫函式 stdlib.h mallo...