Java整個編譯以及運行的過程至關繁瑣,本文經過一個簡單的程序來簡單的說明整個流程。 java
以下圖,Java程序從源文件建立到程序運行要通過兩大步驟:一、源文件由編譯器編譯成字節碼(ByteCode) 二、字節碼由java虛擬機解釋運行。由於java程序既要編譯同時也要通過JVM的解釋運行,因此說Java被稱爲半解釋語言( "semi-interpreted" language)。jvm
圖1 java程序編譯運行過程函數
下面經過如下這個java程序,來講明java程序從編譯到最後運行的整個流程。代碼以下:this
- public class MainApp {
- public static void main(String[] args) {
- Animal animal = new Animal("Puppy");
- animal.printName();
- }
- }
- 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」的錯誤。spa
編譯後的字節碼文件格式主要分爲兩部分:
常量池和
方法字節碼。常量池記錄的是代碼出現過的全部token(類名,成員變量名等等)以及符號引用(方法引用,成員變量引用等等);方法字節碼放的是類中各個方法的字節碼。下面是MainApp.class經過反彙編的結果,咱們能夠清楚看到.class文件的結構:
圖2 MainApp類常量池
圖3 MainApp類方法字節碼
第二步(運行):java類運行的過程大概可分爲兩個過程:一、類的加載 二、類的執行。須要說明的是:JVM主要在程序第一次主動使用類的時候,纔會去加載該類。也就是說,JVM並非在一開始就把一個程序就全部的類都加載到內存中,而是到不得不用的時候才把它加載進來,並且只加載一次。
下面是程序運行的詳細步驟:
- 在編譯好java程序獲得MainApp.class文件後,在命令行上敲java AppMain。系統就會啓動一個jvm進程,jvm進程從classpath路徑中找到一個名爲AppMain.class的二進制文件,將MainApp的類信息加載到運行時數據區的方法區內,這個過程叫作MainApp類的加載。
- 而後JVM找到AppMain的主函數入口,開始執行main函數。
- main函數的第一條命令是Animal animal = new Animal("Puppy");就是讓JVM建立一個Animal對象,可是這時候方法區中沒有Animal類的信息,因此JVM立刻加載Animal類,把Animal類的類型信息放到方法區中。
- 加載完Animal類以後,Java虛擬機作的第一件事情就是在堆區中爲一個新的Animal實例分配內存, 而後調用構造函數初始化Animal實例,這個Animal實例持有着指向方法區的Animal類的類型信息(其中包含有方法表,java動態綁定的底層實現)的引用。
- 當使用animal.printName()的時候,JVM根據animal引用找到Animal對象,而後根據Animal對象持有的引用定位到方法區中Animal類的類型信息的方法表,得到printName()函數的字節碼的地址。
- 開始運行printName()函數。
圖4 java程序運行過程
特別說明:java類中全部public和protected的實例方法都採用動態綁定機制,全部私有方法、靜態方法、構造器及初始化方法<clinit>都是採用靜態綁定機制。而使用動態綁定機制的時候會用到方法表,靜態綁定時並不會用到。本文只是講述java程序運行的大概過程,因此並無細加區分。本文的所述的流程很是粗糙,想深刻了解的讀者請查閱其餘資料。存在謬誤的地方,請多指正。