JVM學習總結--JVM簡介

JVM是什麼

JVM全稱是Java Virtual Machine(Java虛擬機)。它之所以被稱之爲是“虛擬”的,就是因爲它僅僅是由一個規範來定義的抽象計算機。我們平時經常使用的Sun HotSpot虛擬機只是其中一個具體的實現(另外還有BEA JRockit、IBM J9等等虛擬機)。

JVM的設計目標是提供一個基於抽象規格描述的計算機模型,爲解釋程序開發人員提供很好的靈活性,同時也確保Java代碼可在符合該規範的任何系統上運行。JVM對其實現的某些方面給出了具體的定義,特別是對Java可執行代碼,即字節碼(Bytecode)的格式給出了明確的規格。這一規格包括操作碼和操作數的語法和數值、標識符的數值表示方式、以及Java類文件中的Java對象、常量緩衝池在JVM的存儲映象。這些定義爲JVM解釋器開發人員提供了所需的信息和開發環境。Java的設計者希望給開發人員以隨心所欲使用Java的自由。

JVM是java的核心和基礎,在java編譯器和os平臺之間的虛擬處理器。它是一種基於下層的操作系統和硬件平臺並利用軟件方法來實現的抽象的計算機,可以在上面執行java的字節碼程序。

JRE/JDK/JVM是什麼關係

JRE(JavaRuntimeEnvironment,Java運行環境),也就是Java平臺。所有的Java 程序都要在JRE下才能運行。普通用戶只需要運行已開發好的java程序,安裝JRE即可。

JDK(Java Development Kit)是程序開發者用來來編譯、調試java程序用的開發工具包。JDK的工具也是Java程序,也需要JRE才能運行。爲了保持JDK的獨立性和完整性,在JDK的安裝過程中,JRE也是安裝的一部分。所以,在JDK的安裝目錄下有一個名爲jre的目錄,用於存放JRE文件。

JVM(JavaVirtualMachine,Java虛擬機)是JRE的一部分。它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。JVM有自己完善的硬件架構,如處理器、堆棧、寄存器等,還具有相應的指令系統。Java語言最重要的特點就是跨平臺運行。使用JVM就是爲了支持與操作系統無關,實現跨平臺。


JVM的生命週期

當啓動一個Java程序時,一個虛擬機實例也就誕生了。當該程序關閉退出,這個虛擬機實例也就隨之消亡。如果在同一臺計算機上同時運行三個Java程序,將得到三個Java虛擬機實例。每個Java程序都運行於它自己的Java虛擬機實例中。

JVM實例對應了一個獨立運行的java程序,它是進程級別。

1、啓動。

啓動一個Java程序時,一個JVM實例就產生了,任何一個擁有publicstatic void main(String[] args)函數的class都可以作爲JVM實例運行的起點

2、運行。

main()作爲該程序初始線程的起點,任何其他線程均由該線程啓動。JVM內部有兩種線程:守護線程和非守護線程,main()屬於非守護線程,守護線程通常由JVM自己使用,java程序也可以標明自己創建的線程是守護線程

3、消亡。

當程序中的所有非守護線程都終止時,JVM才退出;若安全管理器允許,程序也可以使用Runtime類或者System.exit()來退出。

JVM運行原理

操作系統裝入JVM是通過jdk中Java.exe來完成,通過下面4步來完成JVM環境。

 

1、JVM裝入環境。

JVM提供的方式是操作系統的動態連接文件。既然是文件那就存在一個裝入路徑的問題,Java是怎麼找這個路徑的呢?下面基於Windows的實現的分析。

首先查找jre路徑,Java是通過GetApplicationHomeapi來獲得當前的Java.exe絕對路徑,c:\jdk1.7.0_45\bin\Java.exe,然後截取到絕對路徑c:\jdk1.7.0_45\,判斷c:\jdk1.7.0_45\bin\Java.dll文件是否存在,如果存在就把c:\jdk1.7.0_45\作爲jre路徑,如果不存在則判斷c:\jdk1.7.0_45\jre\bin\Java.dll是否存在,如果存在這c:\jdk1.7.0_45\jre作爲jre路徑,如果不存在調用GetPublicJREHome查HKEY_LOCAL_MACHINE\Software\JavaSoft\JavaRuntime Environment\“當前JRE版本號”\JavaHome的路徑爲jre路徑。

然後裝載JVM.cfg文件。在我們的jdk目錄中jre\bin\server和jre\bin\client都有JVM.dll文件存在,而Java正是通過JVM.cfg配置文件來管理這些不同版本的JVM.dll的。

最後獲得JVM.dll的路徑,JRE路徑+\bin+\JVM類型字符串+\JVM.dll就是JVM的文件路徑了,但是如果在調用Java程序時用-XXaltJVM=參數指定的路徑path,就直接用path+\JVM.dll文件做爲JVM.dll的文件路徑。

 

2、裝載JVM.dll

通過第一步已經找到了JVM的路徑,Java通過LoadJavaVM來裝入JVM.dll文件。裝入工作很簡單,就是調用Windows API函數:

LoadLibrary裝載JVM.dll動態連接庫.然後把JVM.dll中的導出函數JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs掛接到InvocationFunctions變量的CreateJavaVM和GetDefaultJavaVMInitArgs函數指針變量上。JVM.dll的裝載工作宣告完成。

 

3、初始化JVM。

掛接到JNIENV(JNI調用接口)實例,獲得本地調用接口,這樣就可以在Java中調用JVM的函數了。調用InvocationFunctions->CreateJavaVM也就是JVM中JNI_CreateJavaVM方法獲得JNIEnv結構的實例。

 

4、運行Java程序.

Java程序有兩種方式一種是jar包,一種是class。運行jar(Java -jarXXX.jar)的時候,Java.exe調用GetMainClassName函數,該函數先獲得JNIEnv實例然後調用Java類Java.util.jar.JarFileJNIEnv中方法getManifest()並從返回的Manifest對象中取getAttributes("Main-Class")的值即jar包中文件:META-INF/MANIFEST.MF指定的Main-Class的主類名作爲運行的主類。之後main函數會調用Java.c中LoadClass方法裝載該主類(使用JNIEnv實例的FindClass)。main函數直接調用Java.c中LoadClass方法裝載該類。如果是執行class方法。main函數直接調用Java.c中LoadClass方法裝載該類。

然後main函數調用JNIEnv實例的GetStaticMethodID方法查找裝載的class主類中“publicstatic void main(String[] args)”方法,並判斷該方法是否爲public方法,然後調用JNIEnv實例的CallStaticVoidMethod方法調用該Java類的main方法。

JVM體系結構

JVM的基本組成
(1)指令集:JVM指令集
(2)類加載器:在jvm啓動時或者類在運行時將需要的class加載到JVM中
(3)執行引擎:負責執行class文件中的字節碼指令,相當於CPU
(4)運行時數據區:將內存劃分成若干個區,分別完成不同的任務
(5)本地方法區:調用C或C++實現的本地方法代碼返回的結果

  1. 類加載器ClassLoader

    會在下一篇文章中詳細說明ClassLoader加載機制。每一個被JVM裝載的類型都有一個與之對應的Java.lang.Class類的實例來表示該類型。該實例可以唯一表示被jvm裝載的class類,這個實例和其他類的實例一樣放在堆內存中。

       2.執行引擎
        執行引擎相當於線程,是JVM的核心,執行引擎的作用就是解析JVM字節碼指令,得到執行的結果。執行引擎由各個廠家實現。SUN的hotspot是一種基於棧的執行引擎。執行引擎也就是執行一條條代碼的一個流程,代碼都包含在方法體中,執行引擎本質上就是執行一個個方法串起來的流程,對應於操作系統的一個線程,每個java線程就是一個執行引擎的實例。

      

       3.java內存管理
          

          執行引擎在執行的過程中需要存儲一些東西,如操作數,操作碼執行結果,class類的字節碼以及類的對象等信息都需要在執行引擎執行前準備就緒。JVM有一個方法區,java堆區,java棧,PC寄存器和本地方法區。其中方法區和java堆是線程共享的。如果當前線程對應的java棧中沒有棧幀,這個java棧也要被JVM撤銷,整個JVM退出。

JVM選擇基於棧的架構的原因


  JVM執行字節碼指令是基於棧的架構的,所有的操作數必須先入棧,然後根據指令的操作碼選擇從棧頂彈出若干個元素進行計算後再將結果入棧。JVM操作數可以存放在每一個棧幀中的一個本地變量中,即每個方法調用時就會給這個方法分配一個本地變量集,這個本地變量集在編譯時就已經確定,所以操作數入棧可以直接是常量或者從本地變量集中娶一個變量壓入棧中。
  JVM基於棧的設計理由是
  (1)JVM要設計成與平臺無關的,而平臺無關性就要保證在沒有或者由很少的寄存器的機器上也能同樣正確執行java代碼,因爲寄存器很難做到通用。
  (2)基於棧的理由是爲JVM更好地優化代碼而設計的
  (3)爲了指令的緊湊性,因爲java代碼可能在網絡上傳輸,所以class文件的大小也是設計JVM字節碼指令的一個重要因素。


ps: 本文很多是對《深入理解java虛擬機》的總結, 各位如果還有哪裏不明白的,或是我這裏講的還不夠透徹,亦或是講錯了的地方請留言指正,讓我們共同進步,謝謝!


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