標籤:運行 釋放 結構 順序 連結 資料結構 預設 連續 border
根據32位的Windows系統預設有2GB的使用者空間,則不能new超過2GB的,執行下列代碼:
double *p = new double[128*1024*1024*2];
會出現下面的錯誤
error C2148: 數組的總大小不得超過 0x7fffffff 位元組
也就是說數組的總大小不能超過2GB,但實際上剛好小於2GB也是有問題,執行下列語句會出現下列錯誤
double *p = new double[128*1024*(1024*2 -120)];
debug版本下報如下錯誤
release版本下報如下錯誤
並且release和debug模式下堆棧所能分配最大空間也是有區別,經測試release模式要額外需要120m左右的空間,dubug需要320M的空間,這些空間用於分配程式的程式碼片段,靜態區,進程和線程的預設堆棧,應該還有一些連結庫,debug和release模式的區別可能是連結庫的版本不一致,這是自己的理解,不確定對不對。
實際上new調的malloc,Windows上malloc調的HeapAlloc,HeapAlloc最後調的VirtualAlloc。
VirtualAlloc只能以比較大的單位(最小4KB)申請記憶體。HeapAlloc可以申請任意大小的記憶體。malloc基本上可以視為直接調用HeapAlloc(只是基本上)。new在調用malloc之後,如果是類會調用建構函式。
上面說的是堆,下面來說一下棧
每個線程都有一個獨立的棧,預設大小是1M,棧主要用於儲存局部變數和函數參數,例如在某一個函數A裡調用另一個函數B,這兩個函數裡面的變數是共用一個棧,進入函數B時,B中的參數進行壓棧操作,執行完畢返回時再出棧,這時如果參數記憶體過大或者遞迴調用過深,就會使壓棧資料超出棧大小,造成棧溢出,在vs的debug模式下是這樣的,但在release模式下分配超出棧大小資料卻不會報錯,並且實際記憶體也沒有變化,好奇怪。
當然,我們可以修改棧空間的大小,vs中在屬性->連結器->系統->堆棧保留大小可以修改,單位是位元組,該值為0時表示預設1M的空間。
另外函數堆棧的清理方式決定了當函數調用結束時由調用函數或被調用函數來清理函數幀,在VC中對函數棧的清理方式由兩種:
| |
參數行程順序 |
誰負責清理參數佔用的堆棧 |
| __stdcall |
從右至左 |
被調函數 |
| __cdecl |
從右至左 |
調用者 |
堆和棧的區別
1、記憶體空間
棧:在Windows下,棧是向低地址擴充資料結構,是一塊連續記憶體地區。這句話意思是棧頂地址和棧最大容量是系統預先規定好,在WINDOWS下,棧大小是2M(也有說是1M,總之是一個編譯時間就確定常數),如果申請空間超過棧剩餘空間時,將提示overflow。因此,能從棧獲得空間較小。
堆:堆是向高地址擴充資料結構,是不連續記憶體地區。這是由於系統是用鏈表來儲存空閑記憶體位址,自然是不連續,而鏈表遍曆方向是由低地址向高地址。堆大小受限於電腦系統中有效虛擬記憶體。由此可見,堆獲得空間比較靈活,也比較大。
2、分配速度
棧:只要棧剩餘空間大於所申請空間,只需要移動棧頂指標就能完成分配,否則將報異常提示棧溢出。
堆:首先應該知道作業系統有一個記錄空閑記憶體位址鏈表,當系統收到程式申請時,會遍曆該鏈表,尋找第一個空間大於所申請空間堆結點,然後將該結點從空閑結點鏈表中刪除,並將該結點空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中首地址處記錄本次分配大小,這樣,代碼中delete語句才能正確釋放本記憶體空間。另外,由於找到堆結點大小不一定正好等於申請大小,系統會自動將多餘那部分重新放入空閑鏈表中。
堆空間開闢需要用系統函數,棧上直接修改指標.
記憶體配置方式有三種:
1.從靜態儲存地區分配。記憶體在程式編譯時間候就已經分配好,這塊記憶體在程式整個運行期間都存在。例如全域變數,static變數。
2.在棧上建立。在執行函數時,函數內局部變數儲存單元都可以在棧上建立,函數執行結束時這些儲存單元自動被釋放。棧記憶體配置運算內建於處理器指令集中,效率很高,但是分配記憶體容量有限。
3.從堆上分配,亦稱動態記憶體分配。程式在運行時候用malloc或new申請任意多少記憶體,程式員自己負責在何時用free或delete釋放記憶體。動態記憶體生存期由我們決定,使用非常靈活,但問題也最多。
參考地址:http://www.cnblogs.com/yyxt/archive/2015/02/02/4268304.html
c++ new 堆 棧