記憶體分哪些區 C++,ios,java

來源:互聯網
上載者:User

標籤:run   語言   java虛擬機器   結束   空閑   stat   全域變數   調用   http   

韓夢飛沙 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棧的模型:

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

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

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

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

  由於每個線程正在執行的方法可能不同,因此每個線程都會有一個自己的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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.