1. 程式人生 > >JVM——Java虛擬機架構

JVM——Java虛擬機架構

棧幀 區域 反射機制 根據 字節碼 架構 ive 服務 本地

0. 前言

Java虛擬機(Java virtualmachine)實現了Java語言最重要的特征:即平臺無關性。

平臺無關性原理:編譯後的 Java程序(.class文件)由 JVM執行。JVM屏蔽了與具體平臺相關的信息,使程序可以在多種平臺上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。因此實現Java平臺無關性。

本文主要介紹JVM中的架構知識,轉載請註明出處:http://blog.csdn.net/seu_calvin/article/details/51404589

1. JVM結構圖

技術分享

JVM = 類加載器 classloader+ 執行引擎 executionengine + 運行時數據區域 runtime data area

首先Java源代碼文件被Java編譯器編譯為字節碼文件,然後JVM中的類加載器加載完畢之後,交由JVM執行引擎執行。在整個程序執行過程中,JVM中的運行時數據區(內存)會用來存儲程序執行期間需要用到的數據和相關信息。

因此,在Java中我們常常說到的內存管理就是針對這段空間進行管理(如何分配和回收內存空間)。

2. ClassLoader

classloader把硬盤上的class文件加載到JVM中的運行時數據區域,但是它不負責這個類文件能否執行,而這個是執行引擎負責的。

限於篇幅,類加載器的組織結構,加載類的機制原理等會在JVM——類加載器總結一文中描述。

雙親委派模型以及自定義類加載器會在JVM——自定義類加載器一文中描述。

3. 執行引擎

作用:執行字節碼,或者執行本地方法。

4. Runtime DataArea

JVM在運行期間,在運行時數據區對JVM內存空間的劃分和分配,劃分為了以下幾個區域來存儲。

技術分享

(圖註:JDK1.7已經把常量池轉移到堆裏面了!)

PC計數器(The pc Register)

(1)每一個Java線程都有一個PC寄存器,用以記錄比如在線程切換回來後恢復到正確的執行位置。

(2)如該線程正在執行一個Java方法,則計數器記錄的是正在執行的虛擬機字節碼地址,如執行native方法,則計數器值為空。

(3)此內存區域是唯一一個在JVM中沒有規定任何OutOfMemoryError情況的區域。

JVM棧(Java Virtual MachineStacks)

(1)JVM棧是線程私有的,並且生命周期與線程相同。並且當線程運行完畢後,相應內存也就被自動回收。

(2)棧裏面存放的元素叫棧幀,每個方法從調用到執行結束,其實是對應一個棧幀的入棧和出棧。

棧幀用於存儲執行方法時的一些數據,如局部變量表、操作數棧(執行引擎計算時需要),方法出口等等。

(3)這個區域可能有兩種異常:如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常(如:將一個函數反復遞歸自己,最終會出現這種異常)。如果JVM棧可以動態擴展(大部分JVM是可以的),當擴展時無法申請到足夠內存則拋出OutOfMemoryError異常。

本地方法棧(Native Method Stacks)

(1)本地方法棧與虛擬機棧所發揮的作用很相似,他們的區別在於虛擬機棧為執行Java代碼方法服務,而本地方法棧是為Native方法服務。

(2)和JVM棧一樣,這個區域也會拋出StackOverflowError和OutOfMemoryError異常。


方法區(Method Area)

(1)方法區域是全局共享的,比如每個線程都可以訪問同一個類的靜態變量。在方法區中,存儲了已被JVM加載的類的信息、靜態變量、編譯器編譯後的代碼等。如,當程序中通過getName、isInterface等方法來獲取信息時,這些數據來源於方法區。

(2)由於使用反射機制的原因,虛擬機很難推測哪個類信息不再使用,因此這塊區域的回收很難!另外,對這塊區域主要是針對常量池回收,值得註意的是JDK1.7已經把常量池轉移到堆裏面了。

(3)同樣,當方法區無法滿足內存分配需求時,會拋出OutOfMemoryError。

運行時常量池(Runtime Constant Pool)

(1)存放類中固定的常量信息、方法引用信息等,其空間從方法區域(JDK1.7後為堆空間)中分配。

(2)Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有就是常量表,用於存放編譯期已可知的常量,這部分內容將在類加載後進入方法區(永久代)存放。但是Java語言並不要求常量一定只有編譯期預置入Class的常量表的內容才能進入方法區常量池,運行期間也可將新內容放入常量池(最典型的String.intern()方法)。

(3)當常量池無法在申請到內存時會拋出OutOfMemoryError異常,上面也分析過了。

Java堆

(1)Java堆是JVM所管理的最大的一塊內存。它是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。

(2)幾乎所有的實例對象都是在這塊區域中存放。(JIT編譯器貌似不是這樣的)。

(3)Java堆是垃圾收集管理的主要戰場。所有Java堆可以細分為:新生代和老年代。再細致分就是把新生代分為:Eden空間、FromSurvivor空間、To Survivor空間。JVM具體的垃圾回收機制總結請查看我的另外一篇JVM——內存管理和垃圾回收。

(4)根據Java虛擬機規範的規定,Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可。

如果在堆中沒有內存完成實例分配,並且堆也無法再擴展時,將會拋出OutOfMemoryError異常。

5. 堆和棧的區別

這是一個非常常見的面試題,主要從以下幾個方面來回答。

(1)各司其職

最主要的區別就是棧內存用來存儲局部變量和方法調用信息。
而堆內存用來存儲Java中的對象。無論是成員變量、局部變量還是類變量,它們指向的對象都存儲在堆內存中。

(2)空間大小

棧的內存要遠遠小於堆內存,如果你使用遞歸的話,那麽你的棧很快就會充滿並產生StackOverFlowError。
關於如何設置堆棧內存的大小,可以查看JVM——內存管理和垃圾回收中的相關介紹。

(3)獨有還是共享

棧內存歸屬於線程的私有內存,每個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見。
而堆內存中的對象對所有線程可見,可以被所有線程訪問。

(4)異常錯誤

如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常。

如果JVM棧可以動態擴展(大部分JVM是可以的),當擴展時無法申請到足夠內存則拋出OutOfMemoryError異常。

而堆內存沒有可用的空間存儲生成的對象,JVM會拋出java.lang.OutOfMemoryError。

以上便是關於JVM架構的相關知識。

轉載請註明出處:http://blog.csdn.net/seu_calvin/article/details/51404589

JVM——Java虛擬機架構