本文部分內容整理於網絡,感謝原作者。
堆(heap)和棧(stack)是C/C++編程不可避免會碰到的兩個基本概念。首先,這兩個概念都可以在講數據
結構的書中找到,他們都是基本的數據結構,雖然棧更為簡單一些。
在具體的C/C++編程框架中,這兩個概念並不是並行的。對底層機器代碼的研究可以揭示,棧是機器系
統提供的數據結構,而堆則是C/C++函數庫提供的。
具體地說,現代計算機(串行執行機制),都直接在代碼底層支持棧的數據結構。這體現在,有專門的寄
存器指向棧所在的地址,有專門的機器指令完成數據入棧出棧的操作。
這種機制的特點是效率高,支持的數據有限,一般是整數,指針,浮點數等系統直接支持的數據類型,
並不直接支持其他的數據結構。因為棧的這種特點,對棧的使用在程序中是非常頻繁的。對子程序的調
用就是直接利用棧完成的。機器的call指令里隱含了把返回地址推入棧,然后跳轉至子程序地址的操
作,而子程序中的ret指令則隱含從堆棧中彈出返回地址並跳轉之的操作。C/C++中的自動變量是直接利
用棧的例子,這也就是為什么當函數返回時,該函數的自動變量自動失效的原因。
和棧不同,堆的數據結構並不是由系統(無論是機器系統還是操作系統)支持的,而是由函數庫提供的。
基本的malloc/realloc/free函數維護了一套內部的堆數據結構。當程序使用這些函數去獲得新的內存
空間時,這套函數首先試圖從內部堆中尋找可用的內存空間,如果沒有可以使用的內存空間,則試圖利
用系統調用來動態增加程序數據段的內存大小,新分配得到的空間首先被組織進內部堆中去,然后再以
適當的形式返回給調用者。當程序釋放分配的內存空間時,這片內存空間被返回內部堆結構中,可能會
被適當的處理(比如和其他空閑空間合並成更大的空閑空間),以更適合下一次內存分配申請。這套復雜
的分配機制實際上相當於一個內存分配的緩沖池(Cache),使用這套機制有如下若干原因:
1. 系統調用可能不支持任意大小的內存分配。有些系統的系統調用只支持固定大小及其倍數的內存請
求(按頁分配);這樣的話對於大量的小內存分類來說會造成浪費。
2. 系統調用申請內存可能是代價昂貴的。系統調用可能涉及用戶態和核心態的轉換。
3. 沒有管理的內存分配在大量復雜內存的分配釋放操作下很容易造成內存碎片。
堆和棧的對比
從以上知識可知,棧是系統提供的功能,特點是快速高效,缺點是有限制,數據不靈活;而棧是函數庫
提供的功能,特點是靈活方便,數據適應面廣泛,但是效率有一定降低。棧是系統數據結構,對於進
程/線程是唯一的;堆是函數庫內部數據結構,不一定唯一。不同堆分配的內存無法互相操作。棧空間
分靜態分配和動態分配兩種。靜態分配是編譯器完成的,比如自動變量(auto)的分配。動態分配由
alloca函數完成。棧的動態分配無需釋放(是自動的),也就沒有釋放函數。為可移植的程序起見,棧的
動態分配操作是不被鼓勵的!堆空間的分配總是動態的,雖然程序結束時所有的數據空間都會被釋放回
系統,但是精確的申請內存/釋放內存匹配是良好程序的基本要素。
在iOS中堆和棧區別
1.內存管理范圍
- 只有oc對象需要進行內存管理
- 非oc對象類型比如基本數據類型不需要進行內存管理
2.內存管理本質
因為:Objective-C的對象在內存中是以堆的方式分配空間的,並且堆內存是由你釋放的,就是release
OC對象存放於堆里面(堆內存要程序員手動回收)
非OC對象一般放在棧里面(棧內存會被系統自動回收)
堆里面的內存是動態分配的,所以也就需要程序員手動的去添加內存、回收內存
3.內存分配以及管理方式
按分配方式分
- 堆是動態分配和回收內存的,沒有靜態分配的堆
- 棧有兩種分配方式:靜態分配和動態分配
- 靜態分配是系統編譯器完成的,比如局部變量的分配
- 動態分配是有alloc函數進行分配的,但是棧的動態分配和堆是不同的,它的動態分配也由系統編譯器進行釋放,不需要程序員手動管理
按管理方式分
- 對於棧來講,是由系統編譯器自動管理,不需要程序員手動管理
- 對於堆來講,釋放工作由程序員手動管理,不及時回收容易產生內存泄露
網友總結:
堆和棧的區別可以用如下的比喻來看出:
使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等准備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,吃完之后還得清洗、打掃等后續工作(不然用完了不清洗就不好再用),但是比較符合自己的口味,而且自由度大。
棧是吃了吐 ,堆是吃了拉。
參考資料: