Java代碼編譯是由Java源碼編譯器來完成,流程圖以下所示: java
Java字節碼(class文件)的執行是由JVM執行引擎來完成,流程圖以下所示: tomcat
Java代碼編譯和執行的整個過程包含了如下三個重要的機制:jvm
Java 源碼編譯由如下三個過程組成:函數
最後生成的class文件由如下部分組成:this
JVM的類加載是經過ClassLoader及其子類來完成的,類的層次關係和加載順序能夠由下圖來描述: spa
1)Bootstrap ClassLoader 負責加載 $JAVA_HOME中jre/lib/rt.jar裏全部的class,由C++實現,不是ClassLoader子類 2)Extension ClassLoader 負責加載java平臺中擴展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目錄下的jar包 3)App ClassLoader 負責記載classpath中指定的jar包及目錄中class 4)Custom ClassLoader 屬於應用程序根據自身須要自定義的ClassLoader,如tomcat、jboss都會根據j2ee規範自行實現ClassLoader 加載過程當中會先檢查類是否被已加載,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只全部ClassLoader加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。命令行
JVM是基於棧的體系結構來執行class字節碼的。線程建立後,都會產生程序計數器(PC)和棧(Stack),程序計數器存放下一條要執行的指令在方法內的偏移量,棧中存放一個個棧幀,每一個棧幀對應着每一個方法的每次調用,而棧幀又是有局部變量區和操做數棧兩部分組成,局部變量區用於存放方法中的局部變量和參數,操做數棧中用於存放方法執行過程當中產生的中間結果。棧的結構以下圖所示:線程
類被加載到虛擬機內存中開始,到卸載出內存爲主,它的整個生命週期包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個階段。其中驗證、準備、解析3個階段稱爲鏈接(Linking)。調試
以下圖,Java程序從源文件建立到程序運行要通過兩大步驟:一、源文件由編譯器編譯成字節碼(ByteCode) 二、字節碼由java虛擬機解釋運行。由於java程序既要編譯同時也要通過JVM的解釋運行,因此說Java被稱爲半解釋語言。 code
//MainApp.java public class MainApp { public static void main(String[] args) { Animal animal = new Animal("Puppy"); animal.printName(); } } //Animal.java public class Animal { public String name; public Animal(String name) { this.name = name; } public void printName() { System.out.println("Animal ["+name+"]"); } }
第一步(編譯): 建立完源文件以後,程序會先被編譯爲.class文件。Java編譯一個類時,若是這個類所依賴的類尚未被編譯,編譯器就會先編譯這個被依賴的類,而後引用,不然直接引用,這個有點象make。若是java編譯器在指定目錄下找不到該類所其依賴的類的.class文件或者.java源文件的話,編譯器話報「cant find symbol」的錯誤。
編譯後的字節碼文件格式主要分爲兩部分:常量池和方法字節碼。常量池記錄的是代碼出現過的全部token(類名,成員變量名等等)以及符號引用(方法引用,成員變量引用等等);方法字節碼放的是類中各個方法的字節碼。下面是MainApp.class經過反彙編的結果,咱們能夠清楚看到.class文件的結構:
第二步(運行):java類運行的過程大概可分爲兩個過程:一、類的加載 二、類的執行。須要說明的是:JVM主要在程序第一次主動使用類的時候,纔會去加載該類。也就是說,JVM並非在一開始就把一個程序就全部的類都加載到內存中,而是到不得不用的時候才把它加載進來,並且只加載一次。 下面是程序運行的詳細步驟: