Java內存區域劃分(JVM運行時數據區)

        我們在談Java內存區域的劃分時,事實上指的就是JVM內存區域的劃分。在瞭解JVM內存之前,先看一下Java程序具體的執行過程。

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

什麼是JVM?

        JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用於計算設備的規範,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。JVM是運行所有Java程序的虛擬計算機,好比是街機遊戲的模擬器

        JVM屏蔽了與具體操作系統平臺相關的信息,使Java程序只需生成在Java虛擬機上運行的字節碼文件(.class),就可以在多種平臺上不加修改地運行(字節碼文件具有平臺無關性)。JVM在執行字節碼時,實際上最終還是把字節碼解釋成具體平臺上的機器指令執行。

與JRE、JDK的關係?

        JRE(JavaRuntimeEnvironment,Java運行環境),也就是Java平臺。所有的Java 程序都要在JRE下才能運行。

        JDK(Java Development Kit,Java開發工具),是程序開發者用來來編譯、調試java程序用的開發工具包。如javac、java等,JDK中包含JRE。

        JVM是JRE的一部分。JDK>JRE>JVM

JVM的體系結構

        類裝載器(ClassLoader)(用來裝載.class文件)

        執行引擎(執行字節碼,或者執行本地方法)

        運行時數據區(方法區、堆、java棧、程序計數器、本地方法棧)


JVM運行時數據區

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

一、程序計數器(Program Counter Register)

        一塊較小的內存空間,它是當前線程所執行的字節碼的行號指示器,字節碼解釋器工作時通過改變該計數器的值來選擇下一條需要執行的字節碼指令,分支、跳轉、循環等基礎功能都要依賴它來實現。每條線程都有一個獨立的的程序計數器,各線程間的計數器互不影響,因此該區域是線程私有的。

        當線程在執行一個Java方法時,該計數器記錄的是正在執行的虛擬機字節碼指令的地址,當線程在執行的是Native方法(調用本地操作系統方法)時,該計數器的值爲空。另外,該內存區域是唯一一個在Java虛擬機規範中麼有規定任何OOM(內存溢出:OutOfMemoryError)情況的區域。

二、Java虛擬機棧(Java Virtual Machine Stack)

        與程序計數器一樣,Java 虛擬機棧也是線程私有的,它的生命週期與線程相同。Java棧是Java方法執行的內存模型。Java棧中存放的是一個個的棧幀,每個方法被執行的時候都會同時創建一個棧幀,它是用於支持續虛擬機進行方法調用和方法執行的數據結構。對於執行引擎來講,活動線程中,只有棧頂的棧幀是有效的,稱爲當前棧幀,這個棧幀所關聯的方法稱爲當前方法,執行引擎所運行的所有字節碼指令都只針對當前棧幀進行操作。棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法返回地址和一些額外的附加信息。

        1.局部變量表:局部變量表是一組變量值存儲空間,用於存放方法參數和方法內部定義的局部變量,其中存放的數據的類型是編譯期可知的各種基本數據類型、對象引用(reference)和returnAddress類型(它指向了一條字節碼指令的地址)。

        2.操作數棧:操作數棧和局部變量表在訪問方式上存在着較大差異,操作數棧並非採用訪問索引的方式來進行數據訪問的,而是通過標準的入棧和出棧操作來完成一次數據訪問。Java虛擬機的解釋執行引擎稱爲“基於棧的執行引擎”,其中所指的“棧”就是操作數棧。由於程序計數器無法被程序指令直接訪問,Java虛擬機的指令是從操作數棧中取得操作數,所以它的運行方式是基於棧而不是基於寄存器。虛擬機把操作數棧作爲它的工作區,因爲大多數指令都要從這裏彈出數據,執行運算,然後把結果壓回操作數棧

        3.動態鏈接:每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是爲了支持方法調用過程中的動態連接。我們知道Class文件的常量池有存有大量的符號引用,字節碼中的方法調用指令就以常量池中指向方法的符號引用爲參數。這些符號引用一部分會在類加載階段或第一次使用的時候轉化爲直接引用(如final、static域等),這種轉化稱爲靜態解析。另外一部分將在每一次的運行期間轉化爲直接引用,這部分稱爲動態連接。

        4.方法返回地址:當一個方法被執行後,有兩種方式退出該方法,執行引擎遇到返回指令或者是遇到異常,並且異常未在方法內處理。不管哪種情況,一旦方法返回,肯定要獲得返回的地址,來保證正常的執行。而且在返回時可能會保存一些信息,來完成上層的處理。正常退出時,會將程序計數器的值來作爲返回的地址;出現異常時則會通過異常處理器來確定。

三、本地方法棧(Native Method Stack)

        本地方法棧與虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧爲虛擬機執行Java 方法服務,而本地方法棧則爲使用到的本地操作系統(Native)方法服務。

四、Java堆(Java Heap)

        Java堆是Java虛擬機所管理的內存中最大的一塊,它是所有線程共享的一塊內存區域。幾乎所有的對象實例和數組都在這類分配內存。Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱爲“GC堆”。根據Java虛擬機規範的規定,Java堆可以處在物理上不連續的內存空間中,只要邏輯上是連續的即可。如果在堆中沒有內存可分配時,並且堆也無法擴展時,將會拋出OutOfMemoryError異常。

五、方法區(Method Area)

        方法區(Method Area)與Java 堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息(包括類的名稱、方法信息、字段信息)、常量、靜態變量、即時編譯器編譯後的代碼等數據。

        方法區域又被稱爲“永久代”,但這僅僅對於Sun HotSpot來講,JRockit和IBM J9虛擬機中並不存在永久代的概念。Java虛擬機規範把方法區描述爲Java堆的一個邏輯部分,而且它和Java Heap一樣不需要連續的內存,可以選擇固定大小或可擴展,另外,虛擬機規範允許該區域可以選擇不實現垃圾回收。相對而言,垃圾收集行爲在這個區域比較少出現。該區域的內存回收目標主要針是對廢棄常量的和無用類的回收。

        運行時常量池是方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池(Class文件常量池),用於存放編譯器生成的各種字面量和符號引用,這部分內容將在類加載後存放到方法區的運行時常量池中。運行時常量池相對於Class文件常量池的另一個重要特徵是具備動態性,Java語言並不要求常量一定只能在編譯期產生,也就是並非預置入Class文件中的常量池的內容才能進入方法區的運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用比較多的是String類的intern()方法。

        既然運行時常量池是方法區的一部分,自然會受到方法區內存的限制,當常量池無法再申請到內存時會拋出OutOfMemoryError 異常。

 

關於 Object obj = new Object();

        首先obj作爲引用類型(reference)保存在java棧的局部變量表中,其次new Object() 會在堆中開闢一塊內存空間保存obj引用的實例化對象,而該對象實例對應的類的相關信息對象類型數據則保存在方法區,通過指向對象類型數據的指針(指向對象類型數據的指針保存在堆中)訪問方法區中的對象類型數據。


 

參考資料:http://blog.csdn.net/ns_code/article/details/17565503

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章