[整理] 淺談堆、棧、堆區、棧區的概念和區別


一、區別

堆和棧可以分為兩種,一種是數據結構,另一種是和內存的分配有關,這兩種雖然都有棧和堆,但是兩者關系並不大;
1、棧、堆是數據結構里面的叫法;注意:有時候有人喜歡這樣說 "堆棧" 其實說的就是棧而不是堆。  
2、堆區、棧區則是內存模型的叫法。

二、內存中的棧區和堆區

而C語言的內存模型分為5個區:棧區、堆區、靜態區、常量區、代碼區。每個區存儲的內容如下:

1、棧區:存放函數的參數值、局部變量等,由編譯器自動分配和釋放,通常在函數執行完后就釋放了,其操作方式類似於數據結構中的棧。棧內存分配運算內置於CPU的指令集,效率很高,但是分配的內存量有限,比如iOS中棧區的大小是2M。

2、堆區:就是通過new、malloc、realloc分配的內存塊,編譯器不會負責它們的釋放工作,需要用程序區釋放。分配方式類似於數據結構中的鏈表。“內存泄漏”通常說的就是堆區。

3、靜態區:全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束后,由系統釋放。

4、常量區:常量存儲在這里,不允許修改。

5、代碼區:顧名思義,存放代碼。

分布圖:

棧區和堆區大小差異?

棧區:由圖中其實可以知道,棧區是向低地址擴展的,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,大小在進程分配時是確定的,具體大小看編譯器,操作系統。所需大小一般小於10M!太大沒有意義,不符合棧是用來快速存取的目標。

堆區:堆區是向高地址擴展的,是不連續的內存區域(這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的是動態分配的),因為會手動進行分配,會大一些,大小不固定。

棧區和堆區效率差異?

棧區:由系統自動分配,速度較快。但程序員是無法控制的。(只要棧的剩余空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。)

堆區:是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。(首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的 delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中)

小結:其實從上面的知識我們可以看出,如果存放在堆中的數據如果不進行釋放,很可能造成內存泄漏,因為並不一定能觸發gc機制回收。所以對於堆中的內存使用,我們要記得用完釋放。

三、數據結構中的棧和堆

什么是數據結構?

存儲與組織數據的方式。我感覺更應強調數據的組織方式,比如好多數據結構的存儲方式都是用的數組,但他們根據自身的特點進行了封裝,因為存儲方式只有順序存儲和鏈式存儲兩種,但是卻可以組合成多種數據結構。 

常用的數據結構有哪些?

數組、棧、堆、隊列、鏈表等等。

棧是限定僅僅在表尾進行插入和刪除操作的線性表,把允許插入和刪除的一端稱之為棧頂,另外一端稱之為棧底。特點:后進先出,稱之為后進先出線性表。
棧的應用:遞歸。

Java棧

Java棧是一塊線程私有的空間,一個棧,一般由三部分組成:局部變量表、操作數據棧和幀數據區

  • 局部變量表:用於報錯函數的參數及局部變量
  • 操作數棧:主要保存計算過程的中間結果,同時作為計算過程中的變量臨時的存儲空間。
  • 幀數據區:除了局部變量表和操作數據棧以外,棧還需要一些數據來支持常量池的解析,這里幀數據區保存着訪問常量池的指針,方便計程序訪問常量池,另外當函數返回或出現異常時賣虛擬機子必須有一個異常處理表,方便發送異常的時候找到異常的代碼,因此異常處理表也是幀數據區的一部分。

是一種經過排序的樹形數據結構,每一個節點都有一個值,通常所說堆的數據結構是二叉樹,堆的存取是隨意的。所以堆在數據結構中通常可以被看做是一棵樹的數組對象。而且堆需要滿足一下兩個性質:
(1)堆中某個節點的值總是不大於或不小於其父節點的值;
(2)堆總是一棵完全二叉樹。

堆的應用:堆排序,快速找出最大值、最小值,簡化時間復雜度,像這樣支持插入元素和尋找最大(小)值元素的數據結構稱之為優先隊列。

Java 堆

堆內存用於存放由new創建的對象和數組。在堆中分配的內存,由java虛擬機自動垃圾回收器來管理。在堆中產生了一個數組或者對象后,還可以在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,在棧中的這個特殊的變量就變成了數組或者對象的引用變量,以后就可以在程序中使用棧內存中的引用變量來訪問堆中的數組或者對象,引用變量相當於為數組或者對象起的一個別名,或者代號

根據垃圾回收機制的不同,Java堆有可能擁有不同的結構,最為常見的就是將整個Java堆分為

  • 新生代和老年代。其中新聲帶存放新生的對象或者年齡不大的對象,老年代則存放老年對象。

  • 新生代分為eden區、s0區、s1區,s0和s1也被稱為from和to區域,他們是兩塊大小相等並且可以互相角色的空間。

  • 絕大多數情況下,對象首先分配在eden區,在新生代回收后,如果對象還存活,則進入s0或s1區,之后每經過一次

  • 新生代回收,如果對象存活則它的年齡就加1,對象達到一定的年齡后,則進入老年代。

Java方法區

Java方法區和堆一樣,方法區是一塊所有線程共享的內存區域,他保存系統的類信息。

比如類的字段、方法、常量池等。方法區的大小決定系統可以保存多少個類。如果系統定義太多的類,導致方法區溢出。虛擬機同樣會拋出內存溢出的錯誤。方法區可以理解為永久區。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM