工做機制(1)

編譯機制

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

最後生成的class文件由如下部分組成:程序員

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

Java 編譯器把全部的類變量初始化語句和類的靜態初始化塊收集到 <clinit> 方法內,該方法不須要程序員進行顯式調用,只能被 JVM 調用,專門承擔類初始化工做。虛擬機會保證在子類類構造器<clinit>()執行以前,父類的類構造<clinit>()執行完畢。除接口之外,初始化一個類以前必須保證其直接超類已被初始化,而且該初始化過程是由 JVM 保證線程安全的。tomcat

並不是全部的類都會擁有一個 <clinit>方法, 安全

  1. 該類既沒有聲明任何類變量,也沒有靜態初始化語句;
  2. 該類聲明瞭類變量,但沒有明確使用類變量初始化語句或靜態初始化語句 初始化;
  3. 該類僅包含靜態 final 變量的類變量初始化語句,而且初始化的值是編譯時常量表達式。
    eg. 
       public static final String  a =  new Date().toString(); // 有
      public static final String  a =  "1" + "2"; // 無

在一個類的生命週期中,類構造器<clinit>()最多會被虛擬機調用一次,而實例構造器<init>()則會被虛擬機調用屢次,只要程序員還在建立對象。數據結構

類執行機制

  JVM是基於棧的體系結構來執行class字節碼的。線程建立後,都會產生程序計數器(PC)和棧(Stack),程序計數器存放下一條要執行的指令在方法內的偏移量,棧中存放一個個棧幀,每一個棧幀對應着每一個方法的每次調用,而棧幀又是有局部變量區和操做數棧兩部分組成,局部變量區用於存放方法中的局部變量和參數,操做數棧中用於存放方法執行過程當中產生的中間結果。函數

裝載過程

資料引用spa

  1. 加載: 尋找並導入指定類型的二進制文件信息,能夠是class文件、jar 、動態代理生成等等;根據讀取到的字節信息,在方法區建立運行時數據結構:方法列表(保存此類可能調用的全部實例方法的引用 )
  2. 鏈接: 
    1. 驗證: 確保導入的類型正確;
    2. 準備: 在方法區,爲static field分配內存,並設置其初始值。
    3. 解析: 把類文件的常量池部分的符號引用轉化爲運行時常量池的直接引用。
  3. 類初始化:如有靜態初始化過程 調用<clinit>方法

類加載器

Classloader加載類Classs: 文件驗證器檢測編譯版本、常量池、 訪問標識、 字段、方法、指令 等。.net

  1. Bootstrap ClassLoader  啓動類加載器
      $JAVA_HOME中jre/lib/rt.jar裏全部的class,由C++實現,不是ClassLoader子類。
  2. Extension ClassLoader 擴展類加載器
      加載java平臺中擴展功能的一些jar包,包括$JAVA_HOME中jre/lib/ext/*.jar或-Djava.ext.dirs指定目錄下的jar包。
  3. App ClassLoader 系統類加載器
     加載classpath中指定的jar包及目錄中class。
  4. Custom ClassLoader 用戶自定義類加載器(java.lang.ClassLoader的子類)
    應用程序根據自身須要自定義的ClassLoader,如tomcat、jboss都會根據j2ee規範自行實現ClassLoader

雙親委派機制

 加載過程當中會先檢查類是否被已加載,檢查順序是自底向上,從Custom 到BootStrap 逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只全部ClassLoader加載一次。線程

而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。  代理

類的初始化

       java 虛擬機規範爲類的初始化時機作了嚴格定義:在首次主動使用時初始化。這個規則直接影響着類裝載、鏈接和初始化類的機制。由於在類型被初始化以前它必須已經被鏈接,然而在鏈接以前又必須保證它已經被裝載了。

首次主動使用的情形:

  1. 建立某個類的新實例時: new、反射、克隆或反序列化;
  2. 調用某個類的靜態方法時;
  3. 使用某個類或接口的靜態字段或對該字段賦值時(final字段除外);
  4. 調用Java的某些反射方法時;
  5. 初始化某個類的子類;
  6. 在虛擬機啓動時某個含有main()方法的那個啓動類。

對象實例化

 

<clinit>()不須要調用父類的<clinit>(),這是由虛擬機來確保完成的。

【static變量(初始化)、static初始化塊 (內部前後按出現順序)】 -> 【變量、初始化塊(內部前後按出現順序)】> 構造器

父類的類構造器<clinit>() -> 子類的類構造器<clinit>() -> 父類的成員變量和實例代碼塊 -> 父類的構造函數 -> 子類的成員變量和實例代碼塊 -> 子類的構造函數

(注意: 須要有賦值語句纔會初始化)

       Java 編譯器在編譯每一個類時都會爲該類至少生成一個實例"<init>" 初始化方法。包括了全部具備指定初始化值的實例變量初始化語句和java類的構造方法內的全部語句。此方法與源代碼中的每一個構造方法相對應,若是類沒有明確地聲明任何構造方法,編譯器則爲該類生成一個默認的無參構造方法,這個默認的構造器僅僅調用父類的無參構造器,與此同時也會生成一個與默認構造方法對應的 "<init>" 方法。

  1.  一般來講,<init>方法內包括的代碼內容大概爲:調用另外一個 <init>方法;對實例變量初始化;與其對應的構造方法內的代碼。
  2.  若是構造方法是明確地從調用同一個類中的另外一個構造方法開始,那它對應的 <init>方法體內包括的內容爲:一個對本類的 <init>方法的調用;對應構造方法內的全部字節碼。
  3. 若是構造方法不是經過調用自身類的其它構造方法開始,該對象不是 Object 對象,那 <init>法內則包括的內容爲:一個對父類 <init>方法的調用;對實例變量初始化方法的字節碼;最後是對應構造子類的方法體字節碼。若是這個類是 Object,那麼它的 <init>方法則不包括對父類 <init>方法的調用。
相關文章
相關標籤/搜索