(轉載)JVM的內存區域劃分

 

                          JVM的內存區域劃分html

  學過C語言的朋友都知道C編譯器在劃份內存區域的時候常常將管理的區域劃分爲數據段和代碼段,數據段包括堆、棧以及靜態數據區。那麼在Java語言當中,內存又是如何劃分的呢?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規範中雖然規定了程序在執行期間運行時數據區應該包括這幾部分,可是至於具體如何實現並無作出規定,不一樣的虛擬機廠商能夠有不一樣的實現方式。函數

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

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

1.程序計數器

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

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

  因爲在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虛擬機便將運行時常量池從永久代移除了。

  以上爲我的見解和觀點,若有不正之處但願諒解並歡迎指正。

  參考資料:

  http://blog.csdn.net/ns_code/article/details/17565503

  http://www.cnblogs.com/sunada2005/p/3577799.html

  《深刻理解Java虛擬機》

  《Java虛擬機規範 SE7》

  轉載請標明地址:http://www.cnblogs.com/dolphin0520/p/3613043.html

做者: 海子
         
本博客中未標明轉載的文章歸做者 海子和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。
分類: Java基礎
相關文章
相關標籤/搜索