Java基礎-Java編譯詳解

Java基礎-Java編譯詳解

Java源代碼編譯過程

Java代碼編譯是由Java源碼編譯器來完成,流程圖以下所示:前端

Java字節碼的執行是由JVM執行引擎來完成,流程圖以下所示:java

Java代碼編譯和執行的整個過程包含了如下三個重要的機制:後端

  • Java源碼編譯機制
  • 類加載機制
  • 類執行機制

Java源碼編譯機制

Java 源碼編譯由如下三個過程組成:tomcat

  • 分析和輸入到符號表
  • 註解處理
  • 語義分析和生成class文件

流程圖以下所示:優化

img

最後生成的class文件由如下部分組成:spa

  • 結構信息。包括class文件格式版本號及各部分的數量與大小的信息
  • 元數據。對應於Java源碼中聲明與常量的信息。包含類/繼承的超類/實現的接口的聲明信息、域與方法聲明信息和常量池
  • 方法信息。對應Java源碼中語句和表達式對應的信息。包含字節碼、異常處理器表、求值棧與局部變量區大小、求值棧的類型記錄、調試符號信息

Java編譯器

Java編譯器總的來講分爲:線程

  • 前端編譯器:將Java文件編譯爲class文件的編譯器,目前主要有如下兩個,Sun提供的Javac 和Eclipse JDT中的增量式編譯器(ECJ)
  • JIT(just in time compiler)編譯器:虛擬機後端運行期編譯器,把字節碼轉換爲機器碼的過程。HotSpot Vm中提供的C1, C2編譯器
  • AOT(Ahead Of Time Compiler)編譯器:直接把Java文件轉換爲本地機器碼的過程。 GNU Compiler for the java(GCJ), Excelsior JET

在Java3之後爲了使其餘不經過Javac編譯的其餘在java虛擬機上執行的語言能夠享受到編譯器優化所帶來的好處因此,java把全部的編譯器優化工做都放到了後端及時編譯器中,能夠擴展瞭解。調試

類加載機制

JVM的類加載是經過ClassLoader及其子類來完成的,類的層次關係和加載順序能夠由下圖來描述:code

img

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)進行類加載,其加載過程以下:

  1. 若是一個類加載器收到了類加載請求,它首先不會本身去嘗試加載這個類,而是把類加載請求委派給父類加載器去完成。
  2. 每一層的類加載器都把類加載請求委派給父類加載器,直到全部的類加載請求都應該傳遞給頂層的啓動類加載器。
  3. 若是頂層的啓動類加載器沒法完成加載請求,子類加載器嘗試去加載,若是連最初發起類加載請求的類加載器也沒法完成加載請求時,將會拋出ClassNotFoundException,而再也不調用其子類加載器去進行類加載。

雙親委派 模式的類加載機制的優勢是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),程序計數器存放下一條要執行的指令在方法內的偏移量,棧中存放一個個棧幀,每一個棧幀對應着每一個方法的每次調用,而棧幀又是有局部變量區和操做數棧兩部分組成,局部變量區用於存放方法中的局部變量和參數,操做數棧中用於存放方法執行過程當中產生的中間結果。棧的結構以下圖所示:

img

相關文章
相關標籤/搜索