首先了解一下什么是內(nèi)存:
內(nèi)存是計算機(jī)中重要的部件之一,它是與CPU進(jìn)行溝通的橋梁。計算機(jī)中所有程序的運(yùn)行都是在內(nèi)存中進(jìn)行的,因此內(nèi)存的性能對計算機(jī)的影響非常大。內(nèi)存(Memory)也被稱為內(nèi)存儲器,其作用是用于暫時存放CPU中的運(yùn)算數(shù)據(jù),以及與硬盤等外部存儲器交換的數(shù)據(jù)。只要計算機(jī)在運(yùn)行中,CPU就會把需要運(yùn)算的數(shù)據(jù)調(diào)到內(nèi)存中進(jìn)行運(yùn)算,當(dāng)運(yùn)算完成后CPU再將結(jié)果傳送出來,內(nèi)存的運(yùn)行也決定了計算機(jī)的穩(wěn)定運(yùn)行。
內(nèi)存從高地址到低地址的順序依次為:棧區(qū)、堆區(qū)、BSS段、數(shù)據(jù)區(qū)、代碼段
(棧區(qū)是從高地址到低地址存儲,堆區(qū)是從低地址到高地址存儲)
棧區(qū):是在編譯器需要的時候分配,不需要的時候自動釋放和清除變量的存儲區(qū)。棧區(qū)存儲的是局部變量、函數(shù)的參數(shù)等,在一個進(jìn)程中,位于地址空間頂部的是用戶棧,編譯器用它來實(shí)現(xiàn)函數(shù)調(diào)用。
堆區(qū):動態(tài)分配的內(nèi)存。就是在實(shí)例化一個對象的時候,系統(tǒng)分配給對象的內(nèi)存塊,這塊內(nèi)存需要程序員進(jìn)行手動釋放,編譯器并不會自動釋放這塊內(nèi)存,(在實(shí)例化一個對象的時候同時創(chuàng)建一個與類的對象名相同的指針,這個指針存放在棧區(qū),在代碼結(jié)束時編譯器會釋放棧區(qū)的內(nèi)存,這樣對象的指針就不存在了,如果程序員不去手動釋放分配給對象的內(nèi)存塊,就會造成內(nèi)存泄露(Memory leak))
BSS段:存放的是未被初始化的全局變量和靜態(tài)變量。
數(shù)據(jù)區(qū):存放的是已經(jīng)被初始化的全局變量和靜態(tài)變量,還有一些字符常量、常量等。
代碼段:存放的是被編譯之后的二進(jìn)制的代碼的內(nèi)容。
關(guān)于 堆 棧 的區(qū)別
區(qū)分堆和棧,先舉個例子:
class person{
public:
int height;
int weight;
int volume();
}
Person *p = new Person;
上面是實(shí)例化一個對象的例子,首先,我們給對象分配了一塊堆內(nèi)存,在棧內(nèi)存中存放了一個指向?qū)ο蟮亩褍?nèi)存的指針p。堆內(nèi)存申請之后需要程序員的手動釋放(delete p)
堆、棧區(qū)別:
釋放:對于棧區(qū),是由系統(tǒng)自行管理(申請和釋放)。對于堆區(qū),由程序員管理進(jìn)行控制申請和釋放。
(容易產(chǎn)生內(nèi)存泄露)
存儲方向:對于棧區(qū),存儲的方向是向下的,從高地址到低地址存儲。對于堆區(qū),存儲方向是向上的,
從低地址到高低之存儲。
分配:對于棧區(qū),有兩種的分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是由系統(tǒng)完成(局部變量的內(nèi)存分配),
動態(tài)分配由malloc進(jìn)行分配,與堆區(qū)不同,棧區(qū)的動態(tài)分配是系統(tǒng)釋放。對于堆區(qū):都是動態(tài)分配的。
效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,
壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是 C/C++ 函數(shù)庫提供的,它的機(jī)制是很復(fù)雜的,
例如為了分配一塊內(nèi)存,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))
在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用
系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會分到足夠大小的內(nèi)存,然后進(jìn)行返回。
顯然,堆的效率比棧要低得多。
從這里我們可以看到,堆和棧相比,由于大量 new/delete 的使用,容易造成大量的內(nèi)存碎片;
由于沒有專門的系統(tǒng)支持,效率很低;由于可能引發(fā)用戶態(tài)和核心態(tài)的切換,內(nèi)存的申請,代價變得更加昂貴。
所以棧在程序中是應(yīng)用最廣泛的,就算是函數(shù)的調(diào)用也利用棧去完成,函數(shù)調(diào)用過程中的參數(shù),返回地址,
EBP 和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。
雖然棧有如此眾多的好處,但是由于和堆相比不是那么靈活,有時候分配大量的內(nèi)存空間,還是用堆好一些。
無論是堆還是棧,都要防止越界現(xiàn)象的發(fā)生(除非你是故意使其越界),因?yàn)樵浇绲慕Y(jié)果要么是程序崩潰,
要么是摧毀程序的堆、棧結(jié)構(gòu),產(chǎn)生以想不到的結(jié)果,就算是在你的程序運(yùn)行過程中,沒有發(fā)生上面的問題,
你還是要小心.
(new 可以認(rèn)為是 malloc 加構(gòu)造函數(shù)的執(zhí)行,new 出來的指針是直接帶類型信息的,而 malloc 返回的都是 void 指針。在執(zhí)行運(yùn)算符 delete 時相應(yīng)地會調(diào)用對象的析構(gòu)函數(shù),而調(diào)用 free 函數(shù)時則不會——這是很有用的:當(dāng)你類里面包含了其他類的指針變量并且你在你類的析構(gòu)函數(shù)里面釋放了該指針變量,通過執(zhí)行 delete 運(yùn)算符能隱式調(diào)用對象的析構(gòu)函數(shù)來釋放指針變量所指向的空間。)