Java 代碼 編譯和執行過程

流程圖

Java代碼編譯是由Java源碼編譯器來完成,流程圖以下所示: 輸入圖片說明java

Java字節碼(class文件)的執行是由JVM執行引擎來完成,流程圖以下所示: 輸入圖片說明tomcat

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

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

Java源碼編譯機制

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

  • 分析和輸入到符號表
  • 註解處理
  • 語義分析和生成class文件 流程圖以下所示: 輸入圖片說明

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

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

類加載機制

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並非在一開始就把一個程序就全部的類都加載到內存中,而是到不得不用的時候才把它加載進來,並且只加載一次。 下面是程序運行的詳細步驟:

  1. 在編譯好java程序獲得MainApp.class文件後,在命令行上敲java AppMain。系統就會啓動一個jvm進程,jvm進程從classpath路徑中找到一個名爲AppMain.class的二進制文件,將MainApp的類信息加載到運行時數據區的方法區內,這個過程叫作MainApp類的加載。
  2. 而後JVM找到AppMain的主函數入口,開始執行main函數。
  3. main函數的第一條命令是Animal animal = new Animal(「Puppy」);就是讓JVM建立一個Animal對象,可是這時候方法區中沒有Animal類的信息,因此JVM立刻加載Animal類,把Animal類的類型信息放到方法區中。
  4. 加載完Animal類以後,Java虛擬機作的第一件事情就是在堆區中爲一個新的Animal實例分配內存, 而後調用構造函數初始化Animal實例,這個Animal實例持有着指向方法區的Animal類的類型信息(其中包含有方法表,java動態綁定的底層實現)的引用。
  5. 當使用animal.printName()的時候,JVM根據animal引用找到Animal對象,而後根據Animal對象持有的引用定位到方法區中Animal類的類型信息的方法表,得到printName()函數的字節碼的地址。
  6. 開始運行printName()函數。

輸入圖片說明

相關文章
相關標籤/搜索