1. 程式人生 > >內存分哪些區 C++,ios,java

內存分哪些區 C++,ios,java

運行期 不一定 運行時數據區 代碼 -o variables 實現 分布 手動管理內存

韓夢飛沙 yue31313 韓亞飛 han_meng_fei_sha [email protected]

C/C++編譯的程序占用的內存分為以下幾個部分

1、棧區(stack)—由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap)—一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。註意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。
3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。
- 程序結束後由系統釋放。
4、文字常量區—常量字符串就是放在這裏的。程序結束後由系統釋放
5、程序代碼區—存放函數體的二進制代碼。
======

iOS程序中的內存分配分為5大區

  • 內存5大區:堆,棧,方法區,全局區,常量區
  • 棧:不需要手動管理內存,會自動清理棧中的內存
  • 堆: 需要手動管理內存
  • 靜態區:又稱全局區
  • 在計算機中,運行的應用程序的數據都是保存在內存中的,不同類型的數據保存的內存區域不同

內存分區


1.棧區


  • 棧區由編譯器自動分配並釋放,存放函數的參數值,局部變量等。棧是系統數據結構,對應線程/進程是唯一的。

    • 優點是快速高效
    • 缺點是有限制,數據不靈活。[先進後出]
  • 棧空間分靜態分配 和動態分配兩種。

    • 靜態分配是編譯器完成的,比如自動變量(auto)的分配。
    • 動態分配由alloca函數完成。
    • 棧的動態分配無需釋放(是自動的),也就沒有釋放函數。
    • 為可移植的程序起見,棧的動態分配操作是不使用的一般

系統響應

  • 棧:存儲每一個函數在執行的時候都會向操作系統索要資源,棧區就是函數運行時的內存,棧區中的變量由編譯器負責分配和釋放,內存隨著函數的運行分配,隨著函數的結束而釋放,由系統自動完成。
    註意:只要棧的剩余空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。

申請大小的限制

  • 棧是向低地址擴展的數據結構,是一塊連續的內存的區域。是棧頂的地址和棧的最大容量是系統預先規定好的,棧的大小是一個編譯時就確定的常數 ,如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。
  • 內存由系統自動分配,速度快,不會產生內存碎片

2.堆區


  • 堆區由程序員分配和釋放,如果程序員不釋放,程序結束時,可能會由操作系統回收 ,比如在ios 中 alloc 都是存放在堆中。
    • 優點是靈活方便,數據適應面廣泛
    • 缺點是效率有一定降低。[順序隨意]
  • 堆是函數庫內部數據結構,不一定唯一。
    • 不同堆分配的內存無法互相操作。
    • 堆空間的分配總是動態的
      雖然程序結束時所有的數據空間都會被釋放回系統,但是精確的申請內存,釋放內存匹配是良好程序的基本要素。

系統響應

  • 操作系統有一個記錄空閑內存地址的鏈表。
  • 當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序。
  • 由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中

申請大小的限制

  • 堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
  • 有alloc分配的內存,速度較慢,會產生內存碎片,但是使用方便

3. 全局區(靜態區)


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

4.常量區


  • 常量區,存放常量字符串,程序結束後由系統釋放

5.程序代碼區(方法區)


  • 存放函數的二進制代碼
    ]

5大區在內存中分布

技術分享

=============

C編譯器在劃分內存區域的時候經常將管理的區域劃分為數據段和代碼段,數據段包括堆、棧以及靜態數據區。那麽在Java語言當中,內存又是如何劃分的呢?

  由於Java程序是交由JVM執行的,所以我們在談Java內存區域劃分的時候事實上是指JVM內存區域劃分。在討論JVM內存區域劃分之前,先來看一下Java程序具體執行的過程:

   技術分享

  如上圖所示,首先Java源代碼文件(.java後綴)會被Java編譯器編譯為字節碼文件(.class後綴),然後由JVM中的類加載器加載各個類的字節碼文件,加載完畢之後,交由JVM執行引擎執行。在整個程序執行過程中,JVM會用一段空間來存儲程序執行期間需要用到的數據和相關信息,這段空間一般被稱作為Runtime Data Area(運行時數據區),也就是我們常說的JVM內存。因此,在Java中我們常常說到的內存管理就是針對這段空間進行管理(如何分配和回收內存空間)。

  在知道了JVM內存是什麽東西之後,下面我們就來討論一下這段空間具體是如何劃分區域的,是不是也像C語言中一樣也存在棧和堆呢?

一.運行時數據區包括哪幾部分?

  根據《Java虛擬機規範》的規定,運行時數據區通常包括這幾個部分:程序計數器(Program Counter Register)、Java棧(VM Stack)、本地方法棧(Native Method Stack)、方法區(Method Area)、堆(Heap)。

技術分享

  如上圖所示,JVM中的運行時數據區應該包括這些部分。在JVM規範中雖然規定了程序在執行期間運行時數據區應該包括這幾部分,但是至於具體如何實現並沒有做出規定,不同的虛擬機廠商可以有不同的實現方式。

二.運行時數據區的每部分到底存儲了哪些數據?

  下面我們來了解一下運行時數據區的每部分具體用來存儲程序執行過程中的哪些數據。

1.程序計數器

  程序計數器(Program Counter Register),也有稱作為PC寄存器。想必學過匯編語言的朋友對程序計數器這個概念並不陌生,在匯編語言中,程序計數器是指CPU中的寄存器,它保存的是程序當前執行的指令的地址(也可以說保存下一條指令的所在存儲單元的地址),當CPU需要執行指令時,需要從程序計數器中得到當前需要執行的指令所在存儲單元的地址,然後根據得到的地址獲取到指令,在得到指令之後,程序計數器便自動加1或者根據轉移指針得到下一條指令的地址,如此循環,直至執行完所有的指令。

  雖然JVM中的程序計數器並不像匯編語言中的程序計數器一樣是物理概念上的CPU寄存器,但是JVM中的程序計數器的功能跟匯編語言中的程序計數器的功能在邏輯上是等同的,也就是說是用來指示 執行哪條指令的。

  由於在JVM中,多線程是通過線程輪流切換來獲得CPU執行時間的,因此,在任一具體時刻,一個CPU的內核只會執行一條線程中的指令,因此,為了能夠使得每個線程都在線程切換後能夠恢復在切換之前的程序執行位置,每個線程都需要有自己獨立的程序計數器,並且不能互相被幹擾,否則就會影響到程序的正常執行次序。因此,可以這麽說,程序計數器是每個線程所私有的。

  在JVM規範中規定,如果線程執行的是非native方法,則程序計數器中保存的是當前需要執行的指令的地址;如果線程執行的是native方法,則程序計數器中的值是undefined。

  由於程序計數器中存儲的數據所占空間的大小不會隨程序的執行而發生改變,因此,對於程序計數器是不會發生內存溢出現象(OutOfMemory)的。

2.Java棧

  Java棧也稱作虛擬機棧(Java Vitual Machine Stack),也就是我們常常所說的棧,跟C語言的數據段中的棧類似。事實上,Java棧是Java方法執行的內存模型。為什麽這麽說呢?下面就來解釋一下其中的原因。

  Java棧中存放的是一個個的棧幀,每個棧幀對應一個被調用的方法,在棧幀中包括局部變量表(Local Variables)、操作數棧(Operand Stack)、指向當前方法所屬的類的運行時常量池(運行時常量池的概念在方法區部分會談到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些額外的附加信息。當線程執行一個方法時,就會隨之創建一個對應的棧幀,並將建立的棧幀壓棧。當方法執行完畢之後,便會將棧幀出棧。因此可知,線程當前執行的方法所對應的棧幀必定位於Java棧的頂部。講到這裏,大家就應該會明白為什麽 在 使用 遞歸方法的時候容易導致棧內存溢出的現象了以及為什麽棧區的空間不用程序員去管理了(當然在Java中,程序員基本不用關系到內存分配和釋放的事情,因為Java有自己的垃圾回收機制),這部分空間的分配和釋放都是由系統自動實施的。對於所有的程序設計語言來說,棧這部分空間對程序員來說是不透明的。下圖表示了一個Java棧的模型:

技術分享

  局部變量表,顧名思義,想必不用解釋大家應該明白它的作用了吧。就是用來存儲方法中的局部變量(包括在方法中聲明的非靜態變量以及函數形參)。對於基本數據類型的變量,則直接存儲它的值,對於引用類型的變量,則存的是指向對象的引用。局部變量表的大小在編譯器就可以確定其大小了,因此在程序執行期間局部變量表的大小是不會改變的。

  操作數棧,想必學過數據結構中的棧的朋友想必對表達式求值問題不會陌生,棧最典型的一個應用就是用來對表達式求值。想想一個線程執行方法的過程中,實際上就是不斷執行語句的過程,而歸根到底就是進行計算的過程。因此可以這麽說,程序中的所有計算過程都是在借助於操作數棧來完成的。

  指向運行時常量池的引用,因為在方法執行的過程中有可能需要用到類中的常量,所以必須要有一個引用指向運行時常量。

  方法返回地址,當一個方法執行完畢之後,要返回之前調用它的地方,因此在棧幀中必須保存一個方法返回地址。

  由於每個線程正在執行的方法可能不同,因此每個線程都會有一個自己的Java棧,互不幹擾。

3.本地方法棧

  本地方法棧與Java棧的作用和原理非常相似。區別只不過是Java棧是為執行Java方法服務的,而本地方法棧則是為執行本地方法(Native Method)服務的。在JVM規範中,並沒有對本地方發展的具體實現方法以及數據結構作強制規定,虛擬機可以自由實現它。在HotSopt虛擬機中直接就把本地方法棧和Java棧合二為一。

4.堆

  在C語言中,堆這部分空間是唯一一個程序員可以管理的內存區域。程序員可以通過malloc函數和free函數在堆上申請和釋放空間。那麽在Java中是怎麽樣的呢?

  Java中的堆是用來存儲對象本身的以及數組(當然,數組引用是存放在Java棧中的)。只不過和C語言中的不同,在Java中,程序員基本不用去關心空間釋放的問題,Java的垃圾回收機制會自動進行處理。因此這部分空間也是Java垃圾收集器管理的主要區域。另外,堆是被所有線程共享的,在JVM中只有一個堆。

5.方法區

  方法區在JVM中也是一個非常重要的區域,它與堆一樣,是被線程共享的區域。在方法區中,存儲了每個類的信息(包括類的名稱、方法信息、字段信息)、靜態變量、常量以及編譯器編譯後的代碼等。

  在Class文件中除了類的字段、方法、接口等描述信息外,還有一項信息是常量池,用來存儲編譯期間生成的字面量和符號引用。

  在方法區中有一個非常重要的部分就是運行時常量池,它是每一個類或接口的常量池的運行時表示形式,在類和接口被加載到JVM後,對應的運行時常量池就被創建出來。當然並非Class文件常量池中的內容才能進入運行時常量池,在運行期間也可將新的常量放入運行時常量池中,比如String的intern方法。

  在JVM規範中,沒有強制要求方法區必須實現垃圾回收。很多人習慣將方法區稱為“永久代”,是因為HotSpot虛擬機以永久代來實現方法區,從而JVM的垃圾收集器可以像管理堆區一樣管理這部分區域,從而不需要專門為這部分設計垃圾回收機制。不過自從JDK7之後,Hotspot虛擬機便將運行時常量池從永久代移除了。

===========

內存分哪些區 C++,ios,java