C++內(nèi)存管理

內(nèi)存管理是C++最令人頭痛的問題蹲坷,也是C++最有爭議的地方驶乾。C++高手從中獲得了更好的性能,更大的自由循签,C++菜鳥獲取的則是一遍一遍的檢查代碼级乐。而這一切都源于C++內(nèi)存管理的靈活性,其多樣的內(nèi)存分配方式就是其靈活性的最好例證之一县匠。

一個程序要運行风科,就必須先將可執(zhí)行的程序加載到計算機內(nèi)存里,程序加載完畢后乞旦,會形成一個運行空間贼穆,并按照下圖所示進行布局。

內(nèi)存管理.png
  • 代碼區(qū):存放的是程序的執(zhí)行代碼兰粉;
  • 數(shù)據(jù)區(qū):存放的是全局數(shù)據(jù)故痊、常量、靜態(tài)變量等玖姑;
  • 堆區(qū):存放的是動態(tài)內(nèi)存愕秫,供程序隨機申請使用;
  • 棧區(qū):存放的是程序中所用到的局部數(shù)據(jù)焰络。

這些數(shù)據(jù)可以動態(tài)地反應程序中對函數(shù)的調(diào)用狀態(tài)戴甩,通過其軌跡也可以研究其函數(shù)機制。其中闪彼,除了代碼區(qū)不是我們能在代碼中直接控制的等恐,剩余三塊都是我們編碼過程中可以利用的。

在C++中,數(shù)據(jù)區(qū)又被分成自由存儲區(qū)课蔬、全局/靜態(tài)存儲區(qū)和常量存儲區(qū)囱稽,再加上堆區(qū)、棧區(qū)二跋,也就是說內(nèi)存被分成了5個區(qū)战惊。這5種不同的分區(qū)各有所長,適用于不同的情況扎即。

1.C++的內(nèi)存分配方式

1.1 棧區(qū)

在執(zhí)行函數(shù)時吞获,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元將自動被釋放谚鄙。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中各拷,效率很高,但是所分配的內(nèi)存容量有限闷营。

1.2 堆區(qū)

堆是操作系統(tǒng)中的術語烤黍,是操作系統(tǒng)所維護的一塊特殊內(nèi)存,用于程序的內(nèi)存動態(tài)分配傻盟,C語言使用malloc從堆上分配內(nèi)存速蕊,使用free釋放已分配的對應內(nèi)存。

1.3 自由存儲區(qū)

自由存儲區(qū)是C++基于new操作符的一個抽象概念娘赴,凡是通過new操作符進行內(nèi)存申請规哲,該內(nèi)存即為自由存儲區(qū)。

那么自由存儲區(qū)是否能夠是堆(問題等價于new是否能在堆上動態(tài)分配內(nèi)存)诽表,這取決于operator new 的實現(xiàn)細節(jié)唉锌。自由存儲區(qū)不但可以是堆,還可以是靜態(tài)存儲區(qū)竿奏,這要看operator new在哪里為對象分配內(nèi)存糊秆。具體可以參考C++ 自由存儲區(qū)是否等價于堆?這篇文章议双。

1.4 全局/靜態(tài)存儲區(qū)

全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中痘番,在以前的C語言中,全局變量又分為初始化的和未初始化的平痰,在C++里面沒有作此區(qū)分汞舱,它們共同占用同一塊內(nèi)存區(qū)。

1.5 常量存儲區(qū)

比較特殊宗雇,里面存放的是常量昂芜,不允許修改。

2.堆與棧的區(qū)別

上述5種分區(qū)中赔蒲,最常用的就是堆和棧了泌神,最容易混淆的也是它們良漱。所以搞清楚堆和棧的區(qū)別是很有必要的。它們的區(qū)別主要有以下幾個方面

2.1 管理方式

棧是由編譯器自動管理的欢际,無須手工控制母市;堆的釋放工作由程序員控制,容易產(chǎn)生內(nèi)存泄漏损趋。

2.2 空間大小

一般來說在32位系統(tǒng)下患久,堆內(nèi)存可以達到4GB(2^32B = 4 * 2^30B = 4GB)的空間,從這個角度來看堆內(nèi)存幾乎是沒有什么限制的浑槽。但是對于棧來講蒋失,一般都是有一定空間大小的。例如桐玻,VS2017默認椄萃欤空間大小是1M。

2.3 碎片問題

對堆來講镊靴,頻繁的new/delete會造成內(nèi)存空間的不連續(xù)铣卡,從而產(chǎn)生大量的碎片,使程序效率降低邑闲。對于棧來講,則不存在這個問題梧油,因為棧是隊列苫耸,其中的數(shù)據(jù)必須遵循先進后出的規(guī)則,相互之間緊密排列儡陨,絕不會留給其他數(shù)據(jù)可插入之空隙褪子,所以永遠都不可能有一個內(nèi)存塊從棧中間彈出。

2.4 生長方向

對于堆來講骗村,其生長方向是向上的嫌褪,也就是向著內(nèi)存地址增加的方向增長;對于棧來講胚股,它的生長方向是向下的笼痛,是向著內(nèi)存地址減小的方向增長的。

2.5 分配方式

堆都是動態(tài)分配的琅拌,沒有靜態(tài)分配的堆缨伊。棧有兩種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的进宝,比如局部變量的分配刻坊。動態(tài)分配由alloca函數(shù)完成,但是棧的動態(tài)分配和堆是不同的党晋,它的動態(tài)分配是由編譯器進行釋放的谭胚,無須我們手工實現(xiàn)徐块。

2.6 分配效率

棧是系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機會在底層對棧提供支持:它會分配專門的寄存器存放棧的地址灾而,而且壓棧出棧都會有專門的指令來執(zhí)行胡控,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的绰疤,它的機制很復雜铜犬,例如為了分配一塊內(nèi)存,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間轻庆,如果沒有足夠大小的空間(可能是由于內(nèi)存碎片太多)癣猾,則可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機會分到足夠大小的內(nèi)存了余爆,然后返回纷宇。顯然,堆的效率比棧要低得多蛾方。

2.7 總結(jié)

堆和棧相比像捶,由于堆使用了大量new/delete,容易造成大量的內(nèi)存碎片桩砰,而且它沒有專門的系統(tǒng)支持拓春,效率很低,另外它還可能引發(fā)用戶態(tài)和核心態(tài)的切換亚隅,以及內(nèi)存的申請硼莽,代價會變得很高。所以棧在程序中是應用最廣泛的煮纵,就算是函數(shù)的調(diào)用也會利用棧去完成懂鸵,函數(shù)調(diào)用過程中的參數(shù)、返回的地址行疏、EBP和局部變量都是采用棧的方式存放的匆光。所以,推薦大家盡量多用棧酿联,而不是用堆终息。

雖然棧有如此多的好處,但是由于和堆相比它不是那么靈活贞让,有時候會分配大量的內(nèi)存空間采幌,在遇到這種情況時還是用堆好一些。

3.常見的內(nèi)存處理規(guī)則

【規(guī)則1】用malloc或new申請內(nèi)存之后震桶,應該立即檢查指針值是否為NULL休傍。防止使用指針值為NULL的內(nèi)存。

【規(guī)則2】不要忘記為數(shù)組和動態(tài)內(nèi)存賦初值蹲姐。防止將未被初始化的內(nèi)存作為右值使用磨取。

【規(guī)則3】避免數(shù)組或指針的下標越界人柿,特別要當心發(fā)生“多1”或者“少1”操作。

【規(guī)則4】動態(tài)內(nèi)存的申請與釋放必須配對忙厌,防止內(nèi)存泄漏凫岖。

【規(guī)則5】用free或delete釋放了內(nèi)存之后,立即將指針設置為NULL逢净,防止產(chǎn)生“野指針”哥放。


個人主頁:

www.codeapes.cn

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市爹土,隨后出現(xiàn)的幾起案子甥雕,更是在濱河造成了極大的恐慌,老刑警劉巖胀茵,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件社露,死亡現(xiàn)場離奇詭異,居然都是意外死亡琼娘,警方通過查閱死者的電腦和手機峭弟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脱拼,“玉大人瞒瘸,你說我怎么就攤上這事∠ㄅǎ” “怎么了情臭?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長玉组。 經(jīng)常有香客問我谎柄,道長丁侄,這世上最難降的妖魔是什么惯雳? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮鸿摇,結(jié)果婚禮上石景,老公的妹妹穿的比我還像新娘。我一直安慰自己拙吉,他們只是感情好潮孽,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筷黔,像睡著了一般往史。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上佛舱,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天椎例,我揣著相機與錄音挨决,去河邊找鬼。 笑死订歪,一個胖子當著我的面吹牛脖祈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刷晋,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼盖高,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了眼虱?” 一聲冷哼從身側(cè)響起喻奥,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒙幻,沒想到半個月后映凳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡邮破,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年诈豌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抒和。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡矫渔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出摧莽,到底是詐尸還是另有隱情庙洼,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布镊辕,位于F島的核電站油够,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏征懈。R本人自食惡果不足惜石咬,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卖哎。 院中可真熱鬧鬼悠,春花似錦、人聲如沸亏娜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽维贺。三九已至它掂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間溯泣,已是汗流浹背虐秋。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工晰韵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人熟妓。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓雪猪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親起愈。 傳聞我的和親對象是個殘疾皇子只恨,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361