C++ 內存管理

內存分配方式

在C++中,內存分紅5個區,他們分別是堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區。程序員

棧 -- 由編譯器自動分配釋放

在執行函數時,函數內局部變量的存儲單元均可以在棧上建立,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,可是分配的內存容量有限。
是由編譯器在須要時自動分配,不須要時自動清除的變量存儲區。一般存放局部變量、函數參數等。windows

堆 -- 通常由程序員分配釋放

就是那些由new分配的內存塊,他們的釋放編譯器不去管,由咱們的應用程序(程序員)去控制,通常一個new就要對應一個delete,一個new[]與一個delete[]對應。若是程序員沒有釋放掉,那麼在程序結束後,操做系統會自動回收。數組

自由存儲區

就是那些由malloc等分配的內存塊,他和堆是十分類似的,不過它是用free來結束本身的生命的。數據結構

全局/靜態存儲區

全局變量和靜態變量被分配到同一塊內存中(在之前的C語言中,全局變量又分爲初始化的和未初始化的,在C++裏面沒有這個區分了,他們共同佔用同一塊內存區。)函數

常量存儲區

這是一塊比較特殊的存儲區,他們裏面存放的是常量,不容許修改。操作系統

注意:堆和自由存儲區其實不過是同一塊區域,new底層實現代碼中調用了malloc,new能夠當作是malloc智能化的高級版本)指針

棧和堆的討論

堆: 操做系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,而後將該結點從空閒結點鏈表中刪 除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣代碼 中的delete語句才能正確的釋放本內存空間。咱們常說的內存泄露,最多見的就是堆泄露(還有資源泄露),它是指程序在運行中出現泄露,若是程序被關閉掉的話,操做系統會幫助釋放泄露的內存。code

棧: 在函數調用時第一個進棧的主函數中的下一條指令(函數調用語句的下一條可執行語句)的地址而後是函數 的各個參數,在大多數的C編譯器中,參數是由右往左入棧,而後是函數中的局部變量。對象

管理方式:隊列

  • 堆中資源由程序員控制(容易產生memory leak)。
  • 棧資源由編譯器自動管理,無需手工控制。

系統響應:

  • 對於堆,應知道系統有一個記錄空閒內存地址的鏈表,當系統收到程序申請時,遍歷該鏈表,尋找第一個空間大於申請空間的堆結點,刪除空閒結點鏈表中的該結點,並將該結點空間分配給程序(大多數系統會在這塊內存空間首地址記錄本次分配的大小,這樣delete才能正確釋放本內存空間,另外系統會將多餘的部分從新放入空閒鏈表中)。
  • 對於棧,只要棧的剩餘空間大於所申請空間,系統爲程序提供內存,不然報異常提示棧溢出。

空間大小:

  • 堆是不連續的內存區域(由於系統是用鏈表來存儲空閒內存地址,天然不是連續的),堆大小受限於計算機系統中有效的虛擬內存(32bit系統理論上是4G),因此堆的空間比較靈活,比較大。
  • 棧是一塊連續的內存區域,大小是操做系統預約好的,windows下棧大小是2M(也有是1M,在編譯時肯定,VC中可設置)。

碎片問題:

  • 對於堆,頻繁的new/delete會形成大量碎片,使程序效率下降。
  • 對於棧,它是一個先進後出的隊列,進出一一對應,不會產生碎片。

生長方向:

  • 堆向上,向高地址方向增加。
  • 棧向下,向低地址方向增加。

分配方式:

  • 堆都是動態分配(沒有靜態分配的堆)。
  • 棧有靜態分配和動態分配,靜態分配由編譯器完成(如局部變量分配),動態分配由alloca函數分配,但棧的動態分配的資源由編譯器進行釋放,無需程序員實現。

分配效率:

  • 堆由C/C++函數庫提供,機制很複雜。因此堆的效率比棧低不少。
  • 棧是極其系統提供的數據結構,計算機在底層對棧提供支持,分配專門寄存器存放棧地址,棧操做有專門指令。

「野指針」成因主要有兩種:

(1)指針變量沒有被初始化。任何指針變量剛被建立時不會自動成爲NULL指針,它的缺省值是隨機的,它會亂指一氣。因此,指針變量在建立的同時應當被初始化,要麼將指針設置爲NULL,要麼讓它指向合法的內存。

(2)指針p被free或者delete以後,沒有置爲NULL,讓人誤覺得p是個合法的指針。

malloc/free與new/delete的區別:

malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。 它們均可用於申請動態內存和釋放內存。

對於非內部數據類型的對象而言,光用maloc/free沒法知足動態對象的要求。對象在建立的同時要自動執行構造函數,對象在消亡以前要自動執行析構函數。因爲malloc/free是庫函數而不是運算符,不在編譯器控制權限以內,不可以把執行構造函數和析構函數的任務強加於malloc/free。

所以C++語言須要一個能完成動態內存分配和初始化工做的運算符new,以及一個能完成清理與釋放內存工做的運算符delete。注意new/delete不是庫函數。

既然new/delete的功能徹底覆蓋了malloc/free,爲何C++不把malloc/free淘汰出局呢?這是由於C++程序常常要調用C函數,而C程序只能用malloc/free管理動態內存。

若是用free釋放「new建立的動態對象」,那麼該對象因沒法執行析構函數而可能致使程序出錯。若是用delete釋放「malloc申請的動態內存」,結果也會致使程序出錯,可是該程序的可讀性不好。因此new/delete必須配對使用,malloc/free也同樣。

動態分配和釋放內存

C++ 提供了一種「動態內存分配」機制,使得程序能夠在運行期間,根據實際須要,要求操做系統臨時分配一片內存空間用於存放數據。此種內存分配是在程序運行中進行的,而不是在編譯時就肯定的,所以稱爲「動態內存分配」。

在 C++ 中,經過 new 運算符來實現動態內存分配。
new 運算符的第一種用法:

T \*p = new T;
其中,T 是任意類型名,p 是類型爲 T\* 的[指針]


**new 運算符還有第二種用法,用來動態分配一個任意大小的數組:**

T *p =new T[N];
//其中,T 是任意類型名,p 是類型爲 T* 的指針,N 表明「元素個數」,能夠是任何值爲正整數的表達式,表達式中能夠包含變量、函數調用等。這樣的語句動態分配出 N × sizeof(T) 個字節的內存空間,這片空間的起始地址被賦值給 p。

程序從操做系統動態分配所得的內存空間在使用完後應該釋放,交還操做系統,以便操做系統將這片內存空間分配給其餘程序使用。C++ 提供 delete 運算符,用以釋放動態分配的內存空間。delete 運算符的基本用法以下:

delete p;

若是是用 new 的第二種用法分配的內存空間,即動態分配了一個數組,那麼釋放該數組時,應以以下形式使用 delete 運算符:

delete\[\] p;

牢記,用 new 運算符動態分配的內存空間,必定要用 delete 運算符釋放。不然,即使程序運行結束,這部份內存空間仍然不會被操做系統收回,從而成爲被白白浪費掉的內存垃圾。這種現象也稱爲「內存泄露」。

相關文章
相關標籤/搜索