咱們都知道java一直宣傳的口號是:一次編譯,處處運行。那麼它如何實現的呢?看下圖:java
java程序通過一次編譯以後,將java代碼編譯爲字節碼也就是class文件,而後在不一樣的操做系統上依靠不一樣的java虛擬機進行解釋,最後再轉換爲不一樣平臺的機器碼,最終獲得執行。這樣是否是能夠推演,若是要在mac系統上運行,是否是隻須要安裝mac java虛擬機就好了。那麼瞭解了這個基本原理後,咱們嘗試去作更深的研究,一個普通的java程序它的執行流程究竟是怎樣的呢?例如寫了一段這樣的代碼:jvm
public class HelloWorld { public static void main(String[] args) { System.out.print("Hello world"); } }
這段程序從編譯到運行,最終打印出「Hello world」中間通過了哪些步驟呢?直接上圖:學習
java代碼經過編譯以後生成字節碼文件(class文件),經過:java HelloWorld執行,此時java根據系統版本找到jvm.cfg,能夠搜索一下本身電腦上的jvm.cfg文件在哪,它會根據系統版本放在不一樣的位置,打開看一下:spa
這是我電腦上的文件,其中-server KNOWN就表示名稱爲server的jvm可用。若是這時你搜索一下電腦上jvm.dll,就會發現它必定在某個server目錄下。簡而言之就是經過jvm.cfg文件找到對應的jvm.dll,jvm.dll則是java虛擬機的主要實現。接下來會初始化JVM,而且獲取JNI接口,什麼是JNI接口,就是java本地接口,你想啊java被編譯成了class文件,JVM怎麼從硬盤上找到這個文件並裝載到JVM裏呢,就是經過JNI接口(它還經常使用於java與操做系統、硬件交互),找到class文件後並裝載進JVM,而後找到main方法,最後執行。操作系統
可能經過上面的描述,你們對JVM運行流程有了一個粗略的認識,那麼JVM內部究竟是怎麼執行一個class文件的呢,也就是上圖中最後一步第6步的內部細節是怎樣的呢?要了解這個問題,首先得看一下JVM的內部結構:.net
從這個結構不難看出,class文件被jvm裝載之後,通過jvm的內存空間調配,最終是由執行引擎完成class文件的執行。固然這個過程還有其餘角色模塊的協助,這些模塊協同配合才能讓一個java程序成功的運行,下面就詳細介紹這些模板,它們也是後面學習jvm最重要的部分。線程
JVM內存空間包含:方法區、java堆、java棧、本地方法棧。code
方法區是各個線程共享的區域,存放類信息、常量、靜態變量。server
java堆也是線程共享的區域,咱們的類的實例就放在這個區域,能夠想象你的一個系統會產生不少實例,所以java堆的空間也是最大的。若是java堆空間不足了,程序會拋出OutOfMemoryError異常。對象
java棧是每一個線程私有的區域,它的生命週期與線程相同,一個線程對應一個java棧,每執行一個方法就會往棧中壓入一個元素,這個元素叫「棧幀」,而棧幀中包括了方法中的局部變量、用於存放中間狀態值的操做棧,這裏面有不少細節,咱們之後再講。若是java棧空間不足了,程序會拋出StackOverflowError異常,想想什麼狀況下會容易產生這個錯誤,對,遞歸,遞歸若是深度很深,就會執行大量的方法,方法越多java棧的佔用空間越大。
本地方法棧角色和java棧相似,只不過它是用來表示執行本地方法的,本地方法棧存放的方法調用本地方法接口,最終調用本地方法庫,實現與操做系統、硬件交互的目的。
PC寄存器,說到這裏咱們的類已經加載了,實例對象、方法、靜態變量都去了本身改去的地方,那麼問題來了,程序該怎麼執行,哪一個方法先執行,哪一個方法後執行,這些指令執行的順序就是PC寄存器在管,它的做用就是控制程序指令的執行順序。
執行引擎固然就是根據PC寄存器調配的指令順序,依次執行程序指令。
本文主要介紹了java虛擬機運行的基本流程,以及java虛擬機內部結構。下一篇將學習java內存模型以及探索java變量的可見性、有序性、指令重排等問題。