Java 有 GC,C/C++ 需程序員手動管理內存。理解可執行文件靜態結構與進程動態佈局的對應關係,是排查內存洩漏、棧溢出、堆損壞的前提。本文以 GCC 4.4.7 + Linux x86 為例,層層剖析。
$ gcc hello.c -o hello
$ size hello
text data bss dec hex filename
1024 256 128 1408 580 hello
| 段 | 含義 | 典型內容 | 權限 |
|---|---|---|---|
| .text (代碼段) | 機器指令 | 函數代碼、字面常量、只讀數據 | R-X |
| .data (數據段) | 已初始化全局/靜態變量 | int g = 10; static int s = 5; | RW- |
| .bss (未初始化數據段) | 未初始化全局/靜態變量 | int g; static int s;(啟動時自動清零) | RW- |
// 基礎 hello.c
int main() { printf("Hello\n"); return 0; }
// text+4, data+0, bss+0
// + const int c = 42; → text +4 (常量進 .rodata/.text)
// + int g_init = 10; → data +4
// + int g_uninit; → bss +4
// + static int s_init = 5; → data +4
// + static int s_uninit; → bss +4
實測完全吻合理論:常量→代碼段,已初始化全局/靜態→數據段,未初始化→BSS。
+---------------------+ 高地址
| 內核空間 (1GB) | 0xC0000000 ~ 0xFFFFFFFF (32-bit)
+---------------------+
| 用戶棧 ↓ | 增長方向:高→低,存局部變量、返回地址、幀指針
| ... |
| 共享庫映射區 | .so 動態鏈接庫、vdso、vsyscall
| ... |
| 堆 ↑ | 增長方向:低→高,malloc/free/new/delete
| ... |
| .bss | 未初始化全局/靜態
| .data | 已初始化全局/靜態
| .text | 代碼段
+---------------------+ 低地址 (0x08048000 典型入口)
| 區域 | 分配方式 | 釋放方式 | 生命周期 | 典型大小限制 |
|---|---|---|---|---|
| 代碼段 | 加載器映射 | 進程結束 | 整個運行期 | 只讀,共享 |
| 數據段 | 加載器映射 | 進程結束 | 整個運行期 | 可讀寫,私有 |
| BSS | 加載器映射+清零 | 進程結束 | 整個運行期 | 可讀寫,私有 |
| 棧 | 編譯器自動 (函數入口/出口) | 自動 (函數返回) | 函數調用期間 | ~8MB (ulimit -s),向上增長 |
| 堆 | 程序員 malloc/new | 程序員 free/delete | 手動控制 | 虛擬內存上限,向下增長 |
| 維度 | 棧 | 堆 |
|---|---|---|
| 管理者 | 編譯器/CPU (專用指令 push/pop) | C 庫分配器 (ptmalloc/jemalloc/tcmalloc) |
| 碎片 | 無 (LIFO,緊湊) | 有 (外部/內部碎片),需合併/分塊策略 |
| 效率 | 極高 (指針移動) | 較低 (查找空閒塊、鎖競爭、系統調用) |
| 靈活性 | 低 (大小編譯期固定,VLA 除外) | 高 (任意大小、任意生命周期) |
| 錯誤模式 | 棧溢出 (遞歸深、大局部數組) | 洩漏、雙重釋放、Use-After-Free、緩衝區溢出 |
-fsanitize=address,檢測堆/棧/全局緩衝區溢出、UAF、雙重釋放。mallinfo()/malloc_info():運行時查看堆狀態。ulimit -c unlimited + gdb core:棧溢出/段錯誤事後分析。從 .text/.data/.bss 靜態三段,到運行時 棧/堆/共享庫/內核 動態五區,C 程序內存管理鏈路清晰且可控。掌握佈局、生命周期、錯誤模式與工具鏈,才能寫出健壯、高效的系統級代碼。