一、C++ 內存區域
C++ 內存分為 5 個區域:
- 堆 heap :
由 new 分配的內存塊,其釋放編譯器不去管,由程序員自己控制。如果程序員沒有釋放掉,在程序結束時系統會自動回收。涉及的問題:“緩沖區溢出”、“內存泄露”。 - 棧 stack :
是那些編譯器在需要時分配,在不需要時自動清除的存儲區。存放局部變量、函數參數。存放在棧中的數據只在當前函數及下一層函數中有效,一旦函數返回了,這些數據也就自動釋放了。 - 全局/靜態存儲區 (
.bss
段和.data
段) :
全局和靜態變量被分配到同一塊內存中。在 C 語言中,未初始化的放在.bss
段中,初始化的放在.data
段中;在 C++ 里則不區分了。 - 常量存儲區 (
.rodata
段) :
存放常量,不允許修改(通過非正當手段也可以修改)。 - 代碼區 (
.text
段) :
存放代碼(如函數),不允許修改(類似常量存儲區),但可以執行(不同於常量存儲區)。
注意:靜態局部變量也存儲在全局/靜態存儲區,作用域為定義它的函數或語句塊,生命周期與程序一致。
二、Linux內存分步
在 Linux 系統中,程序在內存中的分布如下所示:
低地址
.text---> .data --->.bss
--->heap(堆) --> unused <-- stack(棧)
-->env
高地址
Linux 下 32 位環境的用戶空間內存分布情況圖:
三、堆和棧的區別
- 申請方式不同。
- 棧由系統自動分配。
- 堆是自己申請和釋放的。
- 申請大小限制不同。
- 棧頂和棧底是之前預設好的,棧是向棧底擴展,大小固定,可以通過ulimit -a查看,由ulimit -s修改。
- 堆向高地址擴展,是不連續的內存區域,大小可以靈活調整。
- 申請效率不同。
- 棧由系統分配,速度快,不會有碎片。
- 堆由程序員分配,速度慢,且會有碎片。
- 大小不同。
- 棧由系統分配,速度快,不會有碎片。
- 堆由程序員分配,速度慢,且會有碎片。
1 | 堆 | 棧 |
---|---|---|
管理方式 | 堆中資源由程序員控制(容易產生memory leak) | 棧資源由編譯器自動管理,無需手工控制 |
內存管理機制 | 系統有一個記錄空閑內存地址的鏈表,當系統收到程序申請時,遍歷該鏈表,尋找第一個空間大於申請空間的堆結點,刪 除空閑結點鏈表中的該結點,並將該結點空間分配給程序(大多數系統會在這塊內存空間首地址記錄本次分配的大小,這樣 delete 才能正確釋放本內存空間,另外系統會將多余的部分重新放入空閑鏈表中) | 只要棧的剩余空間大於所申請空間,系統為程序提供內存,否則報異常提示棧溢出。(這一塊理解一下鏈表和隊列的區別,不連續空間和連續空間的區別,應該就比較好理解這兩種機制的區別了) |
空間大小 | 堆是不連續的內存區域(因為系統是用鏈表來存儲空閑內存地址,自然不是連續的),堆大小受限於計算機系統中有效的虛擬內存(32bit 系統理論上是 4G),所以堆的空間比較靈活,比較大 | 棧是一塊連續的內存區域,大小是操作系統預定好的,windows 下棧大小是 2M(也有是 1M,在 編譯時確定,VC 中可設置) |
碎片問題 | 對於堆,頻繁的 new/delete 會造成大量碎片,使程序效率降低 | 對於棧,它是有點類似於數據結構上的一個先進后出的棧,進出一一對應,不會產生碎片。(看到這里我突然明白了為什么面試官在問我堆和棧的區別之前先問了我棧和隊列的區別) |
生長方向 | 堆向上,向高地址方向增長。 | 棧向下,向低地址方向增長。 |
分配方式 | 堆都是動態分配(沒有靜態分配的堆) | 棧有靜態分配和動態分配,靜態分配由編譯器完成(如局部變量分配),動態分配由 alloca 函數分配,但棧的動態分配的資源由編譯器進行釋放,無需程序員實現。 |
分配效率 | 堆由 C/C++ 函數庫提供,機制很復雜。所以堆的效率比棧低很多。 | 棧是其系統提供的數據結構,計算機在底層對棧提供支持,分配專門 寄存器存放棧地址,棧操作有專門指令。 |
參考: