Java虛擬機規范----JVM體系結構


一、Java平台的結構圖

 

二、JVM與JRE、JDK關系?

  JVM:Java Virtual Machine(Java虛擬機),負責執行符合規范的Class文件

  JRE:Java Runtime Environment(java運行環境),包含JVM和類庫

        JDK:Java  Development Kit(java開發工具包),包含JRE和開發工具包,例如javac、javah

相關文章:http://blog.csdn.net/ljheee/article/details/50810570

 

三、JVM所處的位置

 

(1)通常工作中所接觸的基本是Java庫和應用以及Java核心類庫,知道如何使用就可以了,但是歸根結底代碼都是要編譯成class文件由Java虛擬機裝載執行,所產生的結果或者現象都可以通過Java虛擬機的運行機制來解釋。一些相同的代碼會由於虛擬機的實現不同而產生不同結果。

(2)在Java平台的結構中,可以看出,Java虛擬機(JVM)處在核心的位置,是程序與底層操作系統和硬件無關的關鍵。它的下方是移植接口,移植接口由兩部分組成:適配器和Java操作系統,其中依賴於平台的部分稱為適配器;JVM通過移植接口在具體的平台和操作系統上實現;在JVM的上方是Java的基本類庫和擴展類庫以及它們的API, 利用Java API編寫的應用程序(application)和小程序(Java applet)可以在任何Java平台上運行而無需考慮底層平台,就是因為有Java虛擬機(JVM)實現了程序與操作系統的分離,從而實現了Java的平台無關性。

(3)對JVM規范的的抽象說明是一些概念的集合,它們已經在書《The Java Virtual Machine Specification》(《Java虛擬機規范》)中被詳細地描述了;對JVM的具體實現要么是軟件,要么是軟件和硬件的組合,它已經被許多生產廠商所實現,並存在於多種平台之上;運行Java程序的任務由JVM的運行期實例單個承擔。

(4)JVM可以由不同的廠商來實現。由於廠商的不同必然導致JVM在實現上的一些不同,像國內就有著名的TaobaoVM;然而JVM還是可以實現跨平台的特性,這就要歸功於設計JVM時的體系結構了。

(5)JVM在它的生存周期中有一個明確的任務,那就是裝載字節碼文件,一旦字節碼進入虛擬機,它就會被解釋器解釋執行,或者是被即時代碼發生器有選擇的轉換成機器碼執行,即Java程序被執行。因此當Java程序啟動的時候,就產生JVM的一個實例;當程序運行結束的時候,該實例也跟着消失了。

 四、Class字節碼

    編譯后被Java虛擬機所執行的代碼使用了一種平台中立(不依賴於特定硬件及操作系統的)的二進制格式來表示,並且經常(但並非絕對)以文件的形式存儲,因此這種格式被稱為Class文件格式。Class文件格式中精確地定義了類與接口的表示形式,包括在平台相關的目標文件格式中一些細節上的慣例,

正如概念所說,Java為了能夠實現平台無關性,制定了一套自己的二進制格式,並經常以文件的方式存儲,稱為Class文件。這樣在不同平台上,只要都安裝了Java虛擬機,具備Java運行環境[JRE],那么都可以運行相同的Class文件。

上圖描述了Java程序運行的一個全過程,也可以看出Java平台由Java虛擬機和Java應用程序接口搭建,Java語言則是進入這個平台的通道,用Java語言編寫並編譯的程序可以運行在這個平台上。

 

       由Java源文件編譯生成字節碼文件,這個過程非常復雜,學過《編譯原理》的朋友都知道必須經過詞法分析、語法分析、語義分析、中間代碼生成、代碼優化等;同樣的,Java源文件到字節碼的生成也想要經歷這些步驟。Javac編譯器的最后任務就是調用con.sun.tools.javac.jvm.Gen類將這課語法樹編譯為Java字節碼文件。

       其實,所謂的編譯字節碼,無非就是將符合Java語法規范的Java代碼轉化為符合JVM規范的字節碼文件。JVM的架構模型是基於棧的,大部分都需要通過棧來完成。

       字節碼結構比較特殊,其內部不包含任何的分隔符,無法人工區分段落(字節碼文件本身就是給機器讀的),所以無論是字節順序、數量都是有嚴格規定的,所有16位、32位、64位長度的數據都將構造成2個、4個、8個-----8位字節單位來表示,多字節數據項總是按照Big-endian順序(高位字節在地址的最低位,地位字節在地址的最高位)來進行存儲。

       參考《Java虛擬機規范  Java SE7版》的描述,每一個字節碼其實都對應着全局唯一的一個類或者接口的定義信息。字節碼文件才用的是一種類似於C語言結構體的偽結構來描述字節碼文件格式。字節碼文件中對應的“基本類型”u1,u2,u4,u8分別表示無符號1、2、4、8個字節。

Class文件----總體格式

       值得一提的是,一個有效的class字節碼文件的前4個字節為0xCAFEBABE,都是固定的,被稱為“魔術”,即magic。它就是JVM用於校驗所讀取的目標文件是否是一個有效且合法的字節碼文件。由此可見,JVM並不是通過判斷文件后綴名的方式來校驗,以防止人為手動修改。

五、Java虛擬機的體系結構

      一個JVM實例的行為不光是它自己的事,還涉及到它的子系統、存儲區域、數據類型和指令這些部分,它們描述了JVM的一個抽象的內部體系結構,其目的不光規定實現JVM時它內部的體系結構,更重要的是提供了一種方式,用於嚴格定義實現時的外部行為。每個JVM都有兩種機制,一個是裝載具有合適名稱的類(類或是接口),叫做類裝載子系統;另外的一個負責執行包含在已裝載的類或接口中的指令,叫做運行引擎。每個JVM又包括方法區、堆、Java棧、程序計數器和本地方法棧這五個部分,這幾個部分和類裝載機制與運行引擎機制一起組成的體系結構圖為:

 

      Java虛擬機定義了若干種程序運行期間會使用到的運行時數據區,其中有一些會隨着虛擬機啟動而創建,隨着虛擬機退出而銷毀。另外一些則是與線程一一對應的,這些與線程對應的數據區域會隨着線程開始和結束而創建和銷毀。

可以看出Java虛擬機的運行時數據區包括了:方法區、Java堆、Java虛擬機棧、PC寄存器、本地方法棧,還有常量池。它們被分為兩大類-----線程共享、私有數據區。

1.線程共享數據區

包括:Java堆、方法區、常量池。它們會隨着虛擬機啟動而創建,隨着虛擬機退出而銷毀。

(1)Java堆

推薦文章:http://blog.csdn.net/ljheee/article/details/52196455

       Java堆在虛擬機啟動的時候被創建,Java堆主要用來為類實例對象和數組分配內存。Java虛擬機規范並沒有規定對象在堆中的形式。

     在Java中,堆被划分成兩個不同的區域:新生代( Young )、老年代( Old );這也就是JVM采用的“分代收集算法”,簡單說,就是針對不同特征的java對象采用不同的策略實施存放和回收,自然所用分配機制和回收算法就不一樣。新生代( Young ) 又被划分為三個區域:Eden、From Survivor、To Survivor。(《Java虛擬機精講》(高翔龍...)) 

       分代收集算法:采用不同算法處理[存放和回收]Java瞬時對象和長久對象。大部分Java對象都是瞬時對象,朝生夕滅,存活很短暫,通常存放在Young新生代,采用復制算法對新生代進行垃圾回收。老年代對象的生命周期一般都比較長,極端情況下會和JVM生命周期保持一致;通常采用標記-壓縮算法對老年代進行垃圾回收。

       這樣划分的目的是為了使JVM能夠更好的管理堆內存中的對象,包括內存的分配以及回收。

    Java堆可能發生如下異常情況:如果實際所需的堆超過了自動內存管理系統能提供的最大容量,那Java虛擬機將會拋出一個OutOfMemoryError異常。 

(2)方法區

      方法區在虛擬機啟動的時候被創建,它存儲了每一個類的結構信息,例如運行時常量池、字段和方法數據、構造函數和普通方法的字節碼內容、還包括在類、實例、接口初始化時用到的特殊方法。 

      方法區可能發生如下異常情況: 如果方法區的內存空間不能滿足內存分配請求,那Java虛擬機將拋出一個OutOfMemoryError異常. 

(3)常量池

      運行時常量池(Runtime Constant Pool)是每一個類或接口的常量池的運行時表示形式,它包括了若干種不同的常量:從編譯期可知的數值字面量到必須運行期解析后才能獲得的方法或字段引用。運行時常量池在方法區中。

      在創建類和接口的運行時常量池時,可能會發生如下異常情況:當創建類或接口的時候,如果構造運行時常量池所需要的內存空間超過了方法區所能提供的最大值,那Java虛擬機將會拋出一個OutOfMemoryError異常。

2.線程私有數據區

     包括:PC寄存器、JVM棧、本地方法區。它們是與線程一一對應的,這些與線程對應的數據區域會隨着線程開始和結束而創建和銷毀。

(1)PC寄存器

      每個Java虛擬機線程都有自己的PC寄存器。在某個線程被新建時,會獲得一個PC寄存器。線程當前執行的方法稱為當前方法,PC寄存器用來存放當前方法中當前執行的字節碼指令的地址;之所以為每一個線程都分配一個PC寄存器,試想:多線程運行時,某個時間片內只執行一個線程,CPU在不停的切換多個線程,那如何記錄具體每一個線程上一次執行到哪個位置了呢,這時候PC寄存器用來存放當前方法中當前執行的字節碼指令的地址,就完美解決了,這就是為什么PC寄存器是線程私有數據區的原因。

      如果當前方法是本地方法(Native),那么寄存器存放undefined。寄存器的大小至少應該能夠存放一個returnAddress類型的數據或者與平台相關的本地指針的值。

PC寄存器是惟一一個沒有明確規定需要拋出OutOfMemoryError異常的運行時數據區。

(2)JVM棧

       每個Java虛擬機線程都有自己的Java虛擬機棧。Java虛擬機棧用來存放棧幀,而棧幀主要包括了:局部變量表、操作數棧、動態鏈接。Java虛擬機棧允許被實現為固定大小或者可動態擴展的內存大小。

       Java虛擬機使用局部變量表來完成方法調用時的參數傳遞。局部變量表的長度在編譯期已經決定了並存儲於類和接口的二進制表示中,一個局部變量可以保存一個類型為boolean、byte、char、short、float、reference和returnAddress的數據,兩個局部變量可以保存一個類型為long和double的數據。

  Java虛擬機提供一些字節碼指令來從局部變量表或者對象實例的字段中復制常量或變量值到操作數棧中,也提供了一些指令用於從操作數棧取走數據、操作數據和把操作結果重新入棧。在方法調用的時候,操作數棧也用來准備調用方法的參數以及接收方法返回結果。

  每個棧幀中都包含一個指向運行時常量區的引用支持當前方法的動態鏈接。在Class文件中,方法調用和訪問成員變量都是通過符號引用來表示的,動態鏈接的作用就是將符號引用轉化為實際方法的直接引用或者訪問變量的運行是內存位置的正確偏移量。 

        總的來說,Java虛擬機棧是用來存放局部變量和過程結果的地方。 

       Java虛擬機棧可能發生如下異常情況: 如果Java虛擬機棧被實現為固定大小內存,線程請求分配的棧容量超過Java虛擬機棧允許的最大容量時,Java虛擬機將會拋出一個StackOverflowError異常。 

       如果Java虛擬機棧被實現為動態擴展內存大小,並且擴展的動作已經嘗試過,但是目前無法申請到足夠的內存去完成擴展,或者在建立新的線程時沒有足夠的內存去創建對應的虛擬機棧,那Java虛擬機將會拋出一個OutOfMemoryError異常。 

(3)本地方法區

       本地方法棧用於支持native方法的運行。(native方法,比如用C/C++實現的代碼)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM