Java代碼編譯是由Java源碼編譯器來完成,流程圖以下所示:前端
Java字節碼的執行是由JVM執行引擎來完成,流程圖以下所示:java
Java代碼編譯和執行的整個過程包含了如下三個重要的機制:後端
Java 源碼編譯由如下三個過程組成:tomcat
流程圖以下所示:優化
最後生成的class文件由如下部分組成:spa
Java編譯器總的來講分爲:線程
在Java3之後爲了使其餘不經過Javac編譯的其餘在java虛擬機上執行的語言能夠享受到編譯器優化所帶來的好處因此,java把全部的編譯器優化工做都放到了後端及時編譯器中,能夠擴展瞭解。調試
JVM的類加載是經過ClassLoader及其子類來完成的,類的層次關係和加載順序能夠由下圖來描述:code
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加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。
在Java中,任意一個類都須要由加載它的類加載器和這個類自己一同肯定其在java虛擬機中的惟一性,即比較兩個類是否相等,只有在這兩個類是由同一個類加載器加載的前提之下才有意義,不然,即便這兩個類來源於同一個Class類文件,只要加載它的類加載器不相同,那麼這兩個類一定不相等(這裏的相等包括表明類的Class對象的equals()方法、isAssignableFrom()方法、isInstance()方法和instanceof關鍵字的結果)。
從JDK1.2開始,java虛擬機規範推薦開發者使用雙親委派模式(ParentsDelegation Model)進行類加載,其加載過程以下:
雙親委派 模式的類加載機制的優勢是java類它的類加載器一塊兒具有了一種帶優先級的層次關係,越是基礎的類,越是被上層的類加載器進行加載,保證了java程序的穩定運行。雙親委派模式的實現:
loadClass(String name, Boolean resolve) throws ClassNotFoundException{ //首先檢查請求的類是否已經被加載過 Class c = findLoadedClass(name); if(c == null){ try{ if(parent != null){//委派父類加載器加載 c = parent.loadClass(name, false); } else{//委派啓動類加載器加載 c = findBootstrapClassOrNull(name); } }catch(ClassNotFoundException e){ //父類加載器沒法完成類加載請求 } if(c == null){//自己類加載器進行類加載 c = findClass(name); } } if(resolve){ resolveClass(c); } return c; }
這裏須要注意的是在JDK1.2以前,類加載還沒有引入雙親委派模式,所以實現自定義類加載器時經常重寫loadClass方法,提供雙親委派邏輯,從JDK1.2以後,雙親委派模式已經被引入到類加載體系中,自定義類加載器時不須要在本身寫雙親委派的邏輯,所以不鼓勵重寫loadClass方法,而推薦重寫findClass方法。
JVM是基於棧的體系結構來執行class字節碼的。線程建立後,都會產生程序計數器(PC)和棧(Stack),程序計數器存放下一條要執行的指令在方法內的偏移量,棧中存放一個個棧幀,每一個棧幀對應着每一個方法的每次調用,而棧幀又是有局部變量區和操做數棧兩部分組成,局部變量區用於存放方法中的局部變量和參數,操做數棧中用於存放方法執行過程當中產生的中間結果。棧的結構以下圖所示: