1. 程式人生 > >操作系統---棧區與堆區 轉自:https://blog.csdn.net/amcp9/article/details/79597481

操作系統---棧區與堆區 轉自:https://blog.csdn.net/amcp9/article/details/79597481

大量 src 獨立 下一條 拓展 自動分配 原創 工作 detail

當一個程序運行時,其RAM存儲方式是按照一定的區域劃分的,以C為例

技術分享圖片


內存中的棧區處於相對較高的地址向較低的地址拓展,由操作系統決定的最高地址,所以它是一塊連續的內存空間。

棧中分配局部變量空間,堆區是低地址向高地址拓展,用於分配程序員申請的內存空間。另外還有靜態區是分配靜態變量,全局變量空間的;只讀區是分配常量和程序代碼空間的;以及其他一些分區。


棧:

棧是為執行線程留出的內存空間。當函數被調用的時候,棧頂為局部變量和一些 bookkeeping 數據預留塊。當函數執行完畢,塊就沒有用了,可能在下次的函數調用的時候再被使用。棧通常用後進先出(LIFO)的方式預留空間;因此最近的保留塊(reserved block)通常最先被釋放。這麽做可以使跟蹤堆棧變的簡單;從棧中釋放塊(free block)只不過是指針的偏移而已。

1.堆包含一個鏈表來維護已用和空閑的內存塊。在堆上新分配(用 new 或者 malloc)內存是從空閑的內存塊中找到一些滿足要求的合適塊。這個操作會更新堆中的塊鏈表。這些元信息也存儲在堆上,經常在每個塊的頭部一個很小區域。

2.堆的增加新快通常從地地址向高地址擴展。因此你可以認為堆隨著內存分配而不斷的增加大小。如果申請的內存大小很小的話,通常從底層操作系統中得到比申請大小要多的內存。

3.申請和釋放許多小的塊可能會產生如下狀態:在已用塊之間存在很多小的空閑塊。進而申請大塊內存失敗,雖然空閑塊的總和足夠,但是空閑的小塊是零散的,不能滿足申請的大小,。這叫做“堆碎片”。當旁邊有空閑塊的已用塊被釋放時,新的空閑塊可能會與相鄰的空閑塊合並為一個大的空閑塊,這樣可以有效的減少“堆碎片”的產生。

堆:

堆(heap)是為動態分配預留的內存空間。和棧不一樣,從堆上分配和重新分配塊沒有固定模式;你可以在任何時候分配和釋放它。這樣使得跟蹤哪部分堆已經被分配和被釋放變的異常復雜;有許多定制的堆分配策略用來為不同的使用模式下調整堆的性能。

堆和棧是兩種內存分配的兩個統稱。可能有很多種不同的實現方式,但是實現要符合幾個基本的概念:

1.對棧而言,棧中的新加數據項放在其他數據的頂部,移除時你也只能移除最頂部的數據(不能越位獲取)。

2.對堆而言,數據項位置沒有固定的順序。你可以以任何順序插入和刪除,因為他們沒有“頂部”數據這一概念。

堆和棧是一個統稱,可以有很多的實現方式。計算機程序通常有一個棧叫調用棧,用來存儲當前函數調用相關的信息(比如:主調函數的地址,局部變量),因為函數調用之後需要返回給主調函數,也就是說,一旦函數調用返回,局部變量將釋放

棧附屬於線程,因此當線程結束時棧被回收。堆通常通過運行時在應用程序啟動時被分配,當應用程序(進程)退出時被回收。 。當線程被創建的時候,設置棧的大小。在應用程序啟動的時候,設置堆的大小,但是可以在需要的時候擴展(分配器向操作系統申請更多的內存)

棧和堆都是用來從底層操作系統中獲取內存的。

在多線程環境下每一個線程都可以有他自己完全的獨立的棧,但是他們共享堆。並行存取被堆控制而不是棧。

對於C#而言:

托管堆上部署了所有引用類型。這很容易理解。當創建一個應用類型變量時:

object reference = new object();

關鍵字new將在托管堆上分配內存空間,並返回一個該內存空間的地址。左邊的reference位於棧上,是一個引用,存儲著一個內存地址;而這個地址指向的內存(位於托管堆)裏存儲著其內容(一個System.Object的實例)。下面為了方便,簡稱引用類型部署在托管推上

棧與堆的主要區別:

申請方式與回收方式的區別:

棧:棧 是系統自動分配空間的,例如我們定義一個 char a;系統會自動在棧上為其開辟空間。而堆 則是程序員根據需要自己申請的空間,例如malloc或者new。由於棧上的空間是自動分配自動回收的,所以棧上的數據的生存周期只是在函數的運行過程中,運行後就釋放掉,不可以再訪問。

堆:而堆上的數據只要程序員不釋放空間,就一直可以訪問到,不過缺點是一旦忘記釋放會造成內存泄露。

管理方式的區別:

棧:棧編譯器自動管理,無需程序員手工控制

堆:堆空間的申請釋放工作由程序員控制,容易產生內存泄漏。

空間連續性的區別:

棧:棧是向低地址擴展的數據結構,是一塊連續的內存區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩余空間時,將提示溢出。因此,用戶能從棧獲得的空間較小。

堆:堆是向高地址擴展的數據結構,是不連續的內存區域。因為系統是用鏈表來存儲空閑內存地址的,且鏈表的遍歷方向是由低地址向高地址。由此可見,堆獲得的空間較靈活,也較大。


地址的增長方向的區別:

棧:棧的增長方向是向著內存地址減小的方向。

堆:堆的增長方向是向著內存地址增加的方向;

是否產生碎片的區別:

棧:對於棧來講,不會存在這個問題。

堆:對於堆來講,頻繁的malloc/free(new/delete)勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低(雖然程序在退出後操作系統會對內存進行回收管理)。

分配效率的區別:

棧:只要棧的剩余空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。

堆:在堆內存中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由於內存碎片太多),就有需要操作系統來重新整理內存空間,這樣就有機會分到足夠大小的內存,然後返回。顯然,堆的效率比棧要低得多

(同時這也是C#中的裝箱與拆箱損耗性能的原因:裝箱就是把棧上的數據放到堆上,拆箱就是把堆上的數據挪到棧上.這裏多了數據的轉換...需要申請內存等操作)

存儲內容的區別:

值類型的實例通常是在線程棧上分配的(靜態分配),但是在某些情形下可以存儲在堆中。

(同時包含在函數調用時,主函數中函數調用後的下一條指令(函數調用語句的下一條可執行語句)的地址,函數的各個參數,函數中的局部變量。註意靜態變量是不入棧的。 當本次函數調用結束後,按照先進後入的方式直到主函數中的下一條指令,程序由該點繼續運行。 )

引用類型的對象總是在進程堆中分配(動態分配)。
---------------------
作者:amcp9
來源:CSDN
原文:https://blog.csdn.net/amcp9/article/details/79597481
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

操作系統---棧區與堆區 轉自:https://blog.csdn.net/amcp9/article/details/79597481