Java虛擬機及運行時數據區

一、JVM定義

Java虛擬機(Java Virtual Machine),簡稱JVM。當咱們提及Java虛擬機時,可能指的是以下三種不一樣的東西:html

  • 抽象的虛擬機規範
  • 規範的具體實現
  • 一個運行中的虛擬機實例

Java虛擬機抽象規範僅僅是一個概念,在《The Java Virtual Machine Specification》中有詳細的描述。java

該規範的實現,可能來自多個提供商,並存在於多個平臺上,它或者是所有由軟件實現,或者是以硬件和軟件相結合的方式來實現。JVM的實現有不少,廣爲使用的主要有三個(因爲Sun和BEA被Oracle收購,故HotSpot、JRockit已都爲Oracle全部):數組

  • Sun HotSpot(服務端、桌面、嵌入式 通用)
  • BEA JRockit(專一於服務端)
  • IBM J9(服務端、桌面、嵌入式 通用)

當運行一個Java程序的時候,也就在運行一個Java虛擬機實例。注意,咱們所說的Java平臺無關性是指class文件的平臺無關性,JVM是和平臺相關的,不一樣操做系統對應不一樣的JVM。架構

二、JVM架構

HotSpot虛擬機是官方的、最經常使用的JVM規範實現,這裏以HotSpot虛擬機爲例oracle

2.一、HotSpot JVM架構

如圖,JVM能夠分爲三部分:類加載子系統、運行時數據區、執行引擎。spa

(圖片來源:Java Garbage Collection Introduction操作系統

更詳細的結構:.net

(圖片來源:The JVM Architecture Explained線程

2.二、HotSpot JVM在Java SE中的位置

(圖片來源:Jave SE Platform at a Glanceorm

三、JVM運行時數據區

JVM規範定義了若干種程序運行時使用到的運行時數據區:

 分爲兩種,一種隨虛擬機的啓動和退出而建立和銷燬(方法區、堆),一種隨線程的開始和結束而建立和銷燬(虛擬機棧、本地方法棧、程序計數器):

  • 方法區:類信息(版本、字段、方法、接口等描述信息,類的Class對象也存在此)、常量、靜態變量、即時編譯後的代碼、運行時常量池(存放編譯器生成的字面量和符號引用)等。(OutOfMemoryError異常)
  • 堆:實例對象(及數組)。注意不是全部對象,如上面所說類的Class對象存放在方法區。(OutOfMemoryError異常)
  • 棧(HotSpot對虛擬機棧和本地方法棧不加以分區,兩者合二爲一)::存放線程執行產生的棧幀信息(OutOfMemoryError異常、StackOverFlow異常)
    • 虛擬機棧(線程棧): 局部變量表(編譯期可知的基本數據類型、引用類型、returnAddress)、操做數棧、動態連接、方法出口信息等
    • 本地方法棧:與虛擬機棧相似
  • 程序計數器:所屬線程下一條要執行的指令的地址。(沒有規定異常)
方法區、堆區、棧區 都可實現成固定大小容量或實現成可動態擴展容量的。
 

3.一、程序計數器

程序計數器(Program Counter Register)是一塊較小的內存空間,它能夠看作是當前線程所執行的字節碼的信號指示器。若線程正在執行的是一個Java方法,則程序計數器保存的是當前所在線程下一條要執行的字節碼指令的地址;若正在執行的是Native方法,則計數器值爲空(undefined)。

線程私有,每一個JVM線程都有本身的程序寄存器。

此內存區域是惟一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域。

3.二、Java虛擬機棧

Java虛擬機棧(Java Virtual Machine Stack)即線程運行棧,描述的是Java方法執行的內存模型:每一個方法被執行時會同時建立一個棧幀(Stack Frame)用於局部變量表(存編譯期可知的基本數據類型、引用類型、returnAddress)、操做數棧、動態鏈表、方法出口信息等。每個方法被調用直至執行完成的過程就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。

線程私有,每個JVM線程都有本身的java虛擬機棧,這個棧與線程同時建立,它的生命週期與線程相同

JVM stack 能夠實現成固定大小,也能夠實現成可動態擴展的。若實現成固定大小,則每一條線程的JVM Stack容量應該在線程建立時就指定了大小,JVM實現應該提供調節JVM Stack初始容量的手段;若實現成可動態擴展的方式,則應該提供調節最大、最小容量的手段。

能建立的棧的數量(即線程的數量)受所在機器物理內存的限制,但實際上OS對一個進程能使用的內存大小(Windows 2GB)是有限制的,故能建立棧的數量受OS的這個限制。換句話說,JVM並無支持調節各棧能用的總內存而是隻提供了對一個棧大小的調節。

異常(二者實際上有重疊):

StackOverflowError:縱向沒法分配即沒法分配新的棧幀時 拋出此異常。

OutOfMemoryError:橫線沒法分配即沒法創建新的線程(在建立新線程時沒有足夠內存來建立對應的虛擬機棧時 或者 JVM Stack能夠動態擴展但在嘗試擴展時沒法申請到足夠的內存來完成擴展,也就是超過了OS對一個進程能建立的線程數的限制時)時 拋出此異常。

3.三、本地方法棧

Java虛擬機可能會使用到傳統的棧來支持native方法(使用Java語言之外的其它語言編寫的方法)的執行,這個棧就是本地方法棧(Native Method Stack)。

線程私有。

本地方法棧發揮的做用域Java虛擬機棧類似。有的虛擬機(如HotSpot)不對二者加以區分,合二爲一

3.四、方法區

方法區(Method Area)用於存儲已被虛擬機加載的類信息(版本、字段、方法、接口等描述信息)、常量、靜態變量、即時編譯後的代碼、運行時常量池等。

各線程共享。

方法區的容量能夠實現成固定大小的,也能夠實現成可動態擴展的,並在不須要過多空間時候自動收縮。

方法區能夠處於物理上不連續的內存空間中,只要邏輯連續便可。

OutOfMemoryError異常: 方法區的內存空間不能知足內存分配請求,且沒法擴展時拋出此異常。

運行時常量池:存放編譯器生成的各類字面量和符號引用

是方法區的一部分。Class文件編譯後生成的各類字面量和符號引用放在文件的常量池中,這部份內容在類加載後進入方法區的運行時常量池。

與Class文件常量池相比,運行時常量池具有動態性:不要求常量只有在編譯期才能產生,即並不是預置入Class文件中常量池的內容才能進入運行時常量池,運行期間也可將新常量放入運行時常量池,如String的intern()方法(見Java小記-MarchOn)。

OutOfMemoryError異常:當建立類和接口時,若是構造運行時常量池所需的內存空間超過了方法區所能提供的最大內存空間後就會拋出此異常。

注:方法區/永久代/元空間:

方法區常又被稱爲永久代(PermGen space)、元空間(Metaspace),它們並不等價,區別是:方法區爲JVM的規範,永久代、元空間分別是方法區在HotSpot中的一種實現;對於其餘類型的虛擬機實現,如 JRockit(BEA)、J9(IBM),其並無永久代、元空間這些概念,其只要不觸碰到進程可用內存上限便可。

    • 從JDK1.7開始,逐漸開始去永久代工做:JDK1.7中,存儲在永久代的部分數據就已經轉移到了Java Heap或者是 Native Heap。(如符號引用(Symbols)轉移到了native heap;字面量(interned strings)、類的靜態變量(class statics)轉移到了java heap);
    • 從JDK1.8起,改用本地內存實現方法區,稱爲元空間。元空間並不在虛擬機中,而是使用本地內存。所以,默認狀況下,元空間的大小僅受本地內存限制。

3.五、Java堆

Java堆是幾乎全部對象(有例外,如類的Class對象置於方法區)實例分配內存的區域。堆在虛擬機啓動的時候被建立,堆中儲存了各類對象,這些對象被自動內存管理系統(Automatic Storage Management System,也便是常說的「Garbage Collector(垃圾回收器)」)所管理。這些對象無需、也沒法顯示地被銷燬。

各線程共享。

堆的容量能夠實現成固定大小的,也能夠實現成可動態擴展的,並在不須要過多空間時候自動收縮。

Java堆能夠處於物理上不連續的內存空間中,只要邏輯連續便可。

Java 堆異常(OutOfMemoryError):若在堆中沒有足夠內存完成實例分配,而且也沒法擴展時,拋出此異常。

 

3.六、本地直接內存(不是運行時數據區)

本地直接內存(Direct Memory)不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範定義的內存區域。但也可被使用,並且也可能出現OutOfMemoryError異常。其不受堆大小的限制,但受到本機進程可用內存的限制。

四、JVM各區域內存分配參數設置

上述各區域內存大小的配置可參看 HotSpot JVM各區域內存分配參數設置-MarchOn

五、參考資料

[1]http://chenzhou123520.iteye.com/blog/1585224,(其即參考自《深刻理解Java虛擬機——JVM高級特性與最佳實踐》)

相關文章
相關標籤/搜索