可以從兩個所在動態取得記憶體,一般 malloc() 是從 heap,另一款是從 stack。
從 stack 動態配置記憶體,函數結束自動釋出。只是調整 stack 指標,效率足好。
#include <alloca.h>
void *alloca(size_t size);
- 可簡化 longjmp() 或 siglongjmp() 的記憶體釋放,不鼓勵用在其它地方 (為什麼???)
- 不能直接用在函數參數內,配置的記憶體夾在函數參數間可能會有問題。
- stack overflow 時不會傳回 NULL,SIGSEGV
從 heap 動態配置記憶體
#include <stdlib.h>
// 取得 size 大小位元組、未初始化的記憶體空間。
void *malloc(size_t size);
// 從 heap 動態配置的記憶體,都需要用 free() 歸還,或者在程式結束時自動消失。
// 重複釋出可能發生不可預期的結果。ptr 可以是 NULL,不進行任何動作。
void free(void *ptr);
// 基於 malloc(),取得 nmemb * size 大小的記憶體空間,並清為 0。
void *calloc(size_t nmemb, size_t size);
// 記憶體空間重新配置為 size 大小,重複部份會相同。
// 加大配置的空間時,位置可能變或不變。位置改變時會複製重複的部份,效能會較差。
void *realloc(void *ptr, size_t size);
其它使用 malloc() 的函式:strdup() 等。
malloc() 細節
heap 一般放在 Uninitialize data (bss) 後面生向高位址,另一端是 stack 生向 heap。
典型 x86-32 Linux layout
| Kernel |
0xC000 0000 |
argv, environment |
|
Stack |
Top of stack → |
↓
(unallocated memory)
↑ |
|
Program break → |
Heap |
| Uninitialized data (bss) |
| Initialized data |
| Text (program code) |
0x0804 8000 |
|
0x0000 0000 |
|
heap 跟 stack 中間是實際沒用到的記憶體。heap 的盡頭就是 program break,不夠用時會用 sbrk() 調整 program break 來加大 heap,但通常不會縮減 heap。sbrk(0) 傳回目前 program break 位置。
藏在每一個 malloc() 的記憶體前面,會紀錄這段記憶體的大小。當 free() 時,利用記憶體空間形成 doubly link list 歸還回到 free 列表,並嘗試合併相鄰的記憶體區塊。
配置的記憶體區塊 (前置區塊大小) |
size | 可使用的部份 |
|
已釋出可使用的列表 (利用可使用的部份存放 link list 指標) |
size | prev | next | 剩下可使用的部份 |
每次 malloc() 會先從 free 列表取得記憶體,例如用 first-fit 或 best-fit 等演算法,如取不到則嘗試加大 heap。
當配置的記憶體區塊大於 MMAP_THRESHOLD,glibc 改用系統呼叫 mmap() 配置,在 Linux 4.7 之後會受到 RLIMIT_DATA 限制。MMAP_THRESHOLD 預設 128 kB,可透過 mallopt() 調整。
在 multithread 應用,內部使用 mutex 保護相關資料結構,glibc 並建立額外的 memory
allocation arenas。每個 arena 是內部用 brk() 或 mmap() 配置的大記憶體空間。
每個 process 有自己的虛擬記憶體,虛擬記憶體中實際有使用的區塊才會對應到實體記憶體。malloc()
不保證可以取得實體記憶體,萬一沒有實體記憶體體了,有些 process 會被 OOM killer 結束掉 (見 proc 檔案系統
/proc/sys/vm/overcommit_memory 和 /proc/sys/vm/oom_adj 的說明,以及 Linux
kernel 的 Documentation/vm/overcommit-accounting)。
malloc() 除錯
mtrace()、muntrace()
mcheck()、mprobe()
malloc() 除錯函式庫:Electric Fence (http://www.perens.com/FreeSoftware/)、dmalloc (http://dmalloc.com/)、Valgrind (http://valgrind.org/)、Insure++ (http://www.parasoft.com/)。
mallopt()、mallinfo()
參考
- TLPI §7
- man alloca
- malloc() man-page
- asprintf()
- mmap()
- memalign()、posix_memalign()
- malloc_trim()