iOS知識總結(二):堆棧以及內存分配

內存分區(qū)

  • 棧(stack)

    由編譯器自動分配釋放,無需手工管理;
    存放函數(shù)的參數(shù)值、局部變量等;
    操作方式類似于數(shù)據結構中的棧,后入先出;
    棧區(qū)地址從高到低分配,是一塊連續(xù)的內存區(qū)域,棧頂?shù)刂泛腿萘渴窍到y(tǒng)預先規(guī)定好的,因此能獲得的棧的空間較小。

  • 堆(heap)

    用于動態(tài)內存分配;
    由程序員分配釋放,若程序員不釋放,則可能造成內存泄露,程序 結束時有可能由OS回收;
    堆是向高地址擴展的數(shù)據結構,是不連續(xù)的內存區(qū)域,這是由于系統(tǒng)是用鏈表來存儲空閑內存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低到高地址的。堆的大小受制于計算機中的有效虛擬內存,所以堆獲得的空間比較大且靈活。

  • 全局區(qū)/靜態(tài)區(qū)(Data Segment)

    全局變量和靜態(tài)變量在內存中是放在一起的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域;
    該塊內存在程序編譯的時候就已經分配好了,在整個程序運行期間始終不變,程序結束后由系統(tǒng)釋放。

  • 常量區(qū)

    存放常量,不允許修改,常量字符串存放在這里,程序結束后由系統(tǒng)釋放。

  • 代碼區(qū)(text segment)

    存放函數(shù)體的二進制代碼

整個內存區(qū)域由高地址到低地址如下圖所示:


1156719-1d0de5ca1edc35af.png

堆和棧的區(qū)別

  1. 管理方式不同
    棧是由編譯器自動管理,無需程序員手工控制;堆空間的申請和釋放是由程序員控制的,容易產生內存泄露。

  2. 空間大小不同
    棧是由高向低擴展的數(shù)據結構,是一塊連續(xù)的區(qū)域。棧頂?shù)牡刂泛蜅5淖畲笕萘渴怯上到y(tǒng)預先設定好的,當申請的空間超過棧的剩余空間的時候,就提示溢出,所以棧的空間較小。

    堆是由低向高擴展的數(shù)據結構,是不連續(xù)的內存區(qū)域。堆的大小受制于計算機中的有效虛擬內存,所以堆獲得的空間比較大且靈活。

  3. 是否產生碎片
    對于堆來講,頻繁的創(chuàng)建銷毀勢必會造成內存空間的不連續(xù),從而產生大量的碎片,使程序效率降低。而棧則沒有這個問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內存塊從棧中彈出。

  4. 增長方向不同
    棧的增長方向是向下的,是向著內存地址減小的方向;而堆則相反。

  5. 分配方式不同
    堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有兩種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配是有alloc函數(shù)進行分配的,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配由編譯器進行釋放,無需我們手工實現(xiàn)。 棧的分配釋放是由編譯器完成的,棧也有動態(tài)分配,但其和堆是不同的,無需手工實現(xiàn);而堆是動態(tài)分配的。

  6. 分配效率不同
    棧是機器系統(tǒng)提供的數(shù)據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行。
    而堆的機制復雜的多,例如為了分配一塊內存,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據結構/操作系統(tǒng))在堆內存中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由于內存碎片太多),就有需要操作系統(tǒng)來重新整理內存空間,這樣就有機會分到足夠大小的內存,然后返回。顯然,堆的效率比棧要低得多。

其他

之所以分成這么多個區(qū)域,主要基于以下考慮:

一個進程在運行過程中,代碼是根據流程依次執(zhí)行的,只需要訪問一次,當然跳轉和遞歸有可能使代碼執(zhí)行多次,而數(shù)據一般都需要訪問多次,因此單獨開辟空間以方便訪問和節(jié)約空間。

臨時數(shù)據及需要再次使用的代碼在運行時放入棧區(qū)中,生命周期短。

全局數(shù)據和靜態(tài)數(shù)據有可能在整個程序執(zhí)行過程中都需要訪問,因此單獨存儲管理。

堆區(qū)由用戶自由分配,以便管理。

//main.cpp 
int a = 0; 全局初始化區(qū) 
char *p1; 全局未初始化區(qū) 
main() 
{ 
int b; 棧 
char s[] = "abc"; 棧 
char *p2; 棧 
char *p3 = "123456"; 123456\0在常量區(qū),p3在棧上。 
static int c =0; 全局(靜態(tài))初始化區(qū) 
p1 = (char *)malloc(10); 
p2 = (char *)malloc(20); 
分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。 
strcpy(p1, "123456"); 123456\0放在常量區(qū),編譯器可能會將它與p3所指向的"123456"優(yōu)化成一個地方。 
}

棧是一個用來存儲局部和臨時變量的存儲空間。在現(xiàn)代操作系統(tǒng)中,一個線程會分配一個棧. 當一個函數(shù)被調用,一個stack frame(棧幀)就會被壓到stack里。里面包含這個函數(shù)涉及的參數(shù),局部變量,返回地址等相關信息。當函數(shù)返回后,這個棧幀就會被銷毀。而這一切都是自動的,由系統(tǒng)幫我們進行分配與銷毀。對于程序員來說,我們無須自己調度。

在objective-c中只支持一個類型對象:blocks。
關于在block中的對象的生命周期問題。出現(xiàn)這問題的原因是,block是新的對象,當你使用block時候,如果你想對其保持引用,你需要對其進行copy操作,(從棧上copy到堆中,并返回一個指向他的指針),而不是對其進行retain操作

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容