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

Java虛擬機器架構

  1. 前言

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中我們常常說到的記憶體管理就是針對這段空間進行管理(如何分配和回收記憶體空間)。

  1. ClassLoader classloader把硬碟上的class檔案載入到JVM中的執行時資料區域,但是它不負責這個類檔案能否執行,而這個是執行引擎負責的。

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

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

  1. 執行引擎

作用:執行位元組碼,或者執行本地方法。

  1. 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異常。

  1. 堆和棧的區別

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

(1)各司其職

最主要的區別就是棧記憶體用來儲存區域性變數和方法呼叫資訊。 而堆記憶體用來儲存Java中的物件。無論是成員變數、區域性變數還是類變數,它們指向的物件都儲存在堆記憶體中。

(2)空間大小

棧的記憶體要遠遠小於堆記憶體,如果你使用遞迴的話,那麼你的棧很快就會充滿併產生StackOverFlowError。 關於如何設定堆疊記憶體的大小,可以檢視JVM——記憶體管理和垃圾回收中的相關介紹。

(3)獨有還是共享

棧記憶體歸屬於執行緒的私有記憶體,每個執行緒都會有一個棧記憶體,其儲存的變數只能在其所屬執行緒中可見。 而堆記憶體中的物件對所有執行緒可見,可以被所有執行緒訪問。

(4)異常錯誤

如果執行緒請求的棧深度大於虛擬機器所允許的深度,將丟擲StackOverflowError異常。

如果JVM棧可以動態擴充套件(大部分JVM是可以的),當擴充套件時無法申請到足夠記憶體則丟擲OutOfMemoryError異常。

而堆記憶體沒有可用的空間儲存生成的物件,JVM會丟擲java.lang.OutOfMemoryError。