1. 程式人生 > >Java編譯程序和運行過程詳解

Java編譯程序和運行過程詳解

blog oid 分享圖片 類變量 生成 iad itl 目的 拒絕

java整個編譯以及運行的過程相當繁瑣,我就舉一個簡單的例子說明: 編譯原理簡單過程:詞法分析 --> 語法分析 --> 語義分析和中間代碼生成 --> 優化 --> 目標代碼生成 Java程序從源文件創建到程序運行要經過兩大步驟: 1、Java文件會由編譯器編譯成class文件(字節碼文件),會經過編譯原理簡單過程的前三步; 2、字節碼由java虛擬機解釋運行,解釋執行即為目標代碼生成並執行。因為java程序既要編譯的同時也要經過JVM的解釋運行,所以說Java被稱為半解釋語言! ( "semi-interpreted" language) 技術分享圖片
public class Main {
 
    public static void main(String[] args) {
        Animal animal = new Animal("Tom");
        animal.printName();
    }
 
}
 
class Animal{
    private String name;
 
    public Animal(String name) {
        super();
        this.name = name;
    }
     
    public void printName(){
        System.out.println("Animal = " + this.name);
    }
}
第一步(編譯):創建完源文件之後,程序先要被JVM中的java編譯器進行編譯為.class文件。java編譯一個類時,若這個類所依賴的類還沒有被編譯,編譯器會自動的先編譯這個所依賴的類,然後引用;若java編譯器在指定的目錄下找不到該類所依賴的類的 .class文件或者 .java源文件,就會報"Can‘t found sysbol"的異常錯誤。 編譯後的字節碼文件格式主要分為兩部分:常量池和方法字節碼。   常量池記錄的是代碼出現過的字面量(文本字符串、八種基本類型的值、被聲明為final的常量等)以及符號引用(類和方法的全限定名、字段的名稱和描述符、方法的名稱和描述符);   https://www.cnblogs.com/blogtech/p/10000205.html
  方法字節碼中放的是各個方法的字節碼(依賴操作數棧和局部變量表,由JVM解釋執行) 第二步(運行):java類運行的過程大概分為兩個步驟: (1)類的加載   加載 --> 驗證 --> 準備 --> 解析 --> 初始化(其中驗證、準備、解析統稱為類的連接);(參考《深入了解Java虛擬機》)   加載:通過一個類的全限定名來獲取定義此類的二進制字節流(Class文件);將這個二進制字節流所代表的靜態存儲結果轉化為方法區的運行時數據結構;在內存中生成一個java.lang.Class對象,註意:存放在方法區!   驗證:驗證目的是為了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全;使用純粹的Java代碼無法做到諸如訪問數組邊界意外的數據、將一個對象轉型為它未實現的類型、跳轉到不存在的代碼之類的事情,如果這樣做了,編譯器將拒絕編譯!   準備:準備階段是正式為類變量分配內存並設置類變量初始值的階段,這些變量所使用的內存都將在方法區中進行分配。首先這時候進行內存分配的僅包括類變量(static修飾的變量),而不是實例變量,實例變量將會在對象實例化時隨著對象一起分配在Java堆中。
public static int value = 123; 
  變量value在準備階段過後的初始值為0而不是123,因為這時候尚未開始執行任何Java方法,在類初始化的時候才會將value的值賦為123.   解析:解析階段是虛擬機將class常量池內的符號引用替換為直接引用的過程。      符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義的定位到目標即可;      直接引用:是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。有了直接引用,那引用的目標必定已經在內存中存在。   初始化:類初始化階段是類加載過程的最後一步;在準備階段,變量已經賦過一次系統要求的初始值,而在初始化階段,則根據程序員通過程序制定的主觀計劃去初始化類變量和其他資源:初始化階段是執行類構造器<clinit>( )方法的過程。   <clinit>( )方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊(static { }塊)中的語句合並產生的,編譯器收集的順序是由語句在源文件中出現的順序所決定的。 (2)類的執行   需要說明的一點的是:JVM主要在程序第一次運行時主動使用類的時候,才會立即去加載,加載完畢就會生成一個java.lang.Class對象,並且存放在方法區。換言之,JVM並不是在運行時就會把所有使用到的類都加載到內存中,而是用到,不得不加載的時候,才加載進來,而且只加載一次,初始化類構造器<clinit>()方法也只執行一次,所以static{} 塊,類變量賦值語句也就只執行一次,只生成一個java.lang.Class對象!   由Java虛擬機的執行引擎來解釋執行Java字節碼,過程:輸入字節碼文件,字節碼解析,輸出執行完的結果!(不再贅述,請自行參考《深入了解Java虛擬機》) 重點理解:根據上面的程序和概念解釋,詳解該程序運行的詳細步驟 (1)在類路徑下找到編譯好的 java 程序中得到 Test.class 字節碼文件後,在命令行上敲 java Test,系統就會啟動一個 JVM 進程,JVM進程從classpath路徑下找到一個名為Test.class的二進制文件,將Test.class文件中的 類信息加載到運行時數據區的方法區(JDK 8 方法區存在 堆區) 中,這一過程叫做類的加載。(只有類信息在方法區中,才能創建對象,使用類中的成員變量); (2)JVM 找到main方法的主函數入口, 持有一個指向當前類(Test)常量池的指針,而常量池中的第一項發現是一個對Animal對象的符號引用,並且main方法中第一條指令是Animal animal = new Animal("Tom"),就是讓JVM創建一個Animal對象,但是方法區中還沒有Animal類的類信息,於是JVM就要馬上的加載Animal類,將Animal類信息放入到方法區中,於是JVM 以一個直接指向方法區 Animal類的指針(直接引用)替換了常量池中第一項的符號引用。 (3)加載完Animal類的信息以後,JVM虛擬機就會在堆內存中為一個Animal類實例分配內存,然後調用其構造函數初始化Animal實例,這個實例持有指向方法區的Animal類的類型信息(其中包含有方法表,java動態綁定的底層實現)的引用。(animal指向了Animal對象的引用會自動的放在棧中,字符串常量"Tom"會自動的放在方法區的運行時常量池中,對象會自動的放入堆區) (4)當使用 animal.pringName()的時候,JVM根據棧中animal引用找到Animal對象,然後根據Animal對象持有的引用定位到方法區中Animal類的類型信息方法表,獲得pringName()函數的字節碼地址,然後Java虛擬機執行引擎依賴局部變量表,操作數棧進行字節碼解釋執行,返回結果! 技術分享圖片
大家可能對Java執行引擎,結合局部變量表和操作數棧執行字節碼的理解不是很透徹,下來我簡單介紹一下字節碼的執行過程:
public int calc(){
     int a = 100;
     int b = 200;
     int c = 300;
     return (a + b) * c;     
}
字節碼指令展示:   public int calc();   Code:   Stack=2, Locals=4, Args_size=1 //操作棧深度為2和4個Slot局部變量表   0:bipush 100 //將100壓入操作數棧   2:istore_1   //將棧頂100數值存放到局變量Slot,index=1中   3:sipush 200 //將200壓入操作數棧   6:istore_2 //將棧頂200數值存放到局部變量Slot,index=2中   7:sipush 300 //將300壓入操作數棧   10:istore_3 //將棧頂200數值存放到局部變量Slot,index=3中   11:iload_1 //將index=1的局部變量表數值壓入操作數棧(100)   12:iload_2 //將index=2的局部變量表數值壓入操作數棧(200)   13:iadd //取棧頂兩個數值相加,結果壓入操作數棧(300)   14:iload_3 //將index=3的局部變量表數值壓入操作數棧(300)   15:imul //取棧頂兩個數值相乘,結果壓入操作數棧(90000)   16:ireturn //取棧頂數值返回調用者結果 技術分享圖片 局部變量表 index = 0存儲當前對象本身 this 參考資料: https://wenku.baidu.com/view/32208418650e52ea55189863.html http://hxraid.iteye.com/blog/676235 http://hxraid.iteye.com/blog/428891 https://wenku.baidu.com/view/32208418650e52ea55189863.html

Java編譯程序和運行過程詳解