java類的編譯、加載和執行


一、java類的編譯流程

  這里主要講的是從java文件到class文件

  下圖是java類編譯的詳細步驟:

  

  1.詞法分析:將java源代碼的字符流轉變為標記(Token)的集合,Token是編譯過程中的最小元素,關鍵字、變量名、字面量、運算符都可以成為標記。如int a = b + 2 這句代碼包含了6個標記,分別是int 、 a、=、b、+、2。

  2.語法分析:將利用詞法分析后的Token序列構造抽象語法樹的過程。抽象語法樹是一種用來描述程序語法結構的樹的表現方式,語法樹的每一個節點都代表程序代碼中的一個語法結構,例如:包、類型、修飾符、運算符、接口、返回信息、代碼注釋等都是語法結構。

  3.填充符號表:符號表是由一組符號地址和符號信息構成的表格,表中登記的信息在編譯的不同階段都要用到。在語義分析中,符號表所登記的內容將用於語義檢查和產生中間代碼。在目標代碼生成階段,當對符號名進行地址分配時,符號表是地址分配的依據。

  4.注解處理器:JDK1.5加入了對注解的支持,需要在編譯期間對注解進行處理。我們可以將注解處理器看作一組插件,可以修改抽象語法樹中的任意元素。在處理注解的過程中,每次修改抽象語法樹后,編譯器將回到解析及填充符號表的過程重新處理。

  5.語義分析:在語法分析后,編譯器獲得了程序代碼的抽象語法樹表示,語法樹能夠表示一個結構正確的源程序的抽象,但無法保證源程序是符合邏輯的。語義分析的主要任務就是對結構上正確的源程序進行上下文有關的性質審查。

    5.1標注檢查:如變量使用前是否已經被申明,變量賦值類型是否匹配。還有一個重要的動作叫常量折疊,如int i = 1+2; 在常量數上會直接標記為3。

    5.2數據及控制流分析:對程序上下文的邏輯進一步驗證,它可以檢查出如局部變量在使用前是否賦值、方法的每條路徑是否有返回、是否所有的受檢異常都被正確處理了等問題。

    5.3解語法糖:語法糖是在計算機語言中添加某種語法,用來提高程序的可讀性,減少代碼的出錯機會。如泛型、變長參數、自動拆箱和裝箱等。

  6.字節碼生成:字節碼生成階段不僅僅把前面各個步驟生成的信息(語法樹、符號表)轉化為字節碼寫到磁盤中,編寫器還進行了少量的代碼添加和轉換工作。最后輸出字節碼

 

 二、虛擬機的類加載機制

  1.虛擬機把描述類的class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。

  2.類加載過程:

    分別是加載、驗證、准備、解析、初始化。其中驗證、准備和解析統稱為連接;這些步驟中加載、驗證、准備、初始化的順序是確定的,而解析階段則可能在初始化之后。

    

    

    2.1、加載:在加載階段,虛擬機只需要完成下面三件事

      2.1.1、通過類的全限定名來獲取定義此類的二進制流。

        java虛擬機並沒有規定從哪加載二進制流,可以是壓縮包中,可從網絡中,可用從數據庫中,也可以是計算機運算生成(動態代理產生的代理類)等。

      2.1.2、將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。

        虛擬機外部的二進制字節流就按照虛擬機所需的格式存儲在方法區中,方法區中的數據存儲格式由虛擬機實現自定義。

      2.1.3、在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。

        這里並沒有規定存在java堆中,在HotSpot中它是存儲在方法區里面。

    2.2、驗證:驗證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,並且不會危害到自身的安全

      驗證大致會分為4個階段的校驗動作:

      2.2.1、文件格式驗證:主要驗證字節流是否符合Class文件的規范,並且能夠被當前版本的虛擬機處理等。該階段的驗證主要是保證輸入的自己流能夠正確的解析並且存儲於方法區之內。只有通過了驗證后,字節流才會進入內存的方法區中進行存儲。

      2.2.2、元數據驗證:對字節碼描述的信息進行語義分析,以保證描述的信息符合java語言規范的要求。

      2.2.3、字節碼驗證:主要的目的是通過對數據流和控制流分析,確定程序語義是合法的、符合邏輯的。確保被驗證的類不會危害虛擬機的安全。

      2.2.4、符號引用驗證:這個階段發生在虛擬機將符號引用轉化為直接引用的時候,這個轉化動作將在連接的第三階段-----解析中發生。符號引用驗證可以看作對類自身以外的信息進行匹配校驗。

    2.3、准備:正式為類變量(靜態變量)分配初始內存並設置初始值(並不是代碼的初始賦值)的階段,這些變量所使用的內存都將在方法區中進行分配。如果是常量則直接分配為常量值。

    

 

    2.4、解析:將虛擬機中常量池內的符號引用替換為直接引用的過程。

      2.4.1、符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能夠無歧義定位到目標即可。

      2.4.2、直接引用:直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。

    2.5、初始化:初始化是類加載的最后一步,前面的類加載的過程中,除了在加載階段用戶應用程序可以通過自定義類加載器參與外,其余動作完全由虛擬機主導和控制。到了初始化階段,才真正開始執行類中定義的java程序代碼。

  

三、雙親委派模型

       

 

   從java虛擬機的角度講,只存在兩種類加載器:一種是啟動加載器(Bootstrap ClassLoader),這個加載器使用C++語言實現,是虛擬機自身的一部分;另外的加載器都是由java語言實現,獨立於虛擬機外部,並且都繼承自抽象類java.lang.ClassLoader。

   啟動類加載器(Bootstrap ClassLoader):負責加載虛擬機啟動所需要的類庫。它只能加載自己能夠識別的類。

     擴展類加載器(Extendtion ClassLoader):它負責加載<JAVA_HOME>\lib\ext目錄中的或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以使用擴展類加載器。

    應用程序類加載器(Application ClassLoader):負責加載CLassPath上所指定的類庫,如果應用程序沒有自定義過自己的類加載器,一般情況下這就是程序的默認類加載器。

    自定義類加載器(User ClassLoader):自己定義的類加載器

  

  雙親委派模型要求除了頂層的啟動類加載器外,其余的類加載器都應該有自己的父類加載器。並且類加載器之間的父子關系不是通過繼承來實現的,而是通過組合關系來復用父類加載器的代碼。

  雙親委派模型的工作過程是如果一個類加載器收到類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,因此所有的加載請求最終都會委派到啟動類加載器中。只有父類加載器反饋自己無法加載這個類時,子類加載器才會嘗試自己去加載。

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM