JVM是如何進行類加載的?

一個類只有被JVM虛擬機加載過後才能使用,虛擬機加載一個類需要進過:加載、鏈接、初始化這三步驟才能被使用。鏈接又分爲:驗證、準備、解析三個步驟。下面我將圍繞這六個步驟進行講解。

       加載:虛擬機將Class文件中的字節碼加載到內存中,那虛擬機是如何將字節碼加載到內存中的呢?答案是通過類加載器,說到類加載器,所有的類加載器都有一個共同的祖師爺(啓動類加載器),啓動類加載是由C++實現的,因此它沒有對應的Java對象,所以在Java中只能用Null來指代,除了啓動類加載器之外,其他的類加載器都是java.lang.ClassLoader的子類,因此有對應的Java對象,使用類加載器需要先加載它的父類類加載後才能使用,比如:只有啓動類加載器,加載到虛擬機中後,其他的類加載器才能進行加載,當一個類需要加載時,類加載器會先讓父類加載器加載,如果父類加載器無法加載,子類加載器纔會嘗試加載,這個規則叫雙親委派模型

       啓動類加載器:加載的都是最基礎、最重要的類,比如jre的lib目錄下的jar包中的類,除了啓動類加載器之外,還有擴展類加載器和應用類加載器,這三個類加載器是最重要的三個。

      擴展類加載器:它的父類加載器是啓動類加載器,它主要負責加載相對於通用、次要的類,比如:jre的lib\ext目錄下的jar包中的類。(注意,在Java9以後引入了模塊系統,擴展類加載器改名爲平臺類加載器,所以,除了少數幾個模塊之外,其他模塊都是由平臺類加載器進行加載的

     應用類加載器:它主要加載應用程序下的類,

    鏈接:鏈接可以分爲:驗證、準備、解析三個階段,Java虛擬機規範並沒有要求在鏈接過程中完成解析。它僅規定了:如果某些字節碼使用了符號引用,那麼在執行這些字節碼之前,需要完成對這些符號引用的解析。

        驗證階段主要是驗證字節碼是否滿足Java虛擬機的約束條件,

       準備階段主要是爲類的靜態字段分配內存,當然除了給靜態字段分配內存之外,部分Java虛擬機還會在此階段構造其他跟類層次相關的數據結構,比如實現虛方法的動態綁定的方法表,

         解析是將符號引用解析成實際引用,當解析的這個符號引用所對應的類如果沒有被加載,那麼這裏就會觸發這個類的加載,但是未必會觸發鏈接和初始化。(符號引用就是在Class文件被加載到Java虛擬機之前,這個類無法知道其他類以及其方法、字段所對應的具體地址,甚至不知道自己的方法、字段的地址,因此每當需要引用這些方法、字段或者類時,Java編譯器會生成一個符號引用來代指。

  初始化:類加載的最後一步便是初始化,只要初始化以後這個類就可以被使用了,初始化主要是爲標記爲常量值的字段賦值,以及執行<clinit>方法的過程,在Java代碼中,如果要初始化一個靜態字段,我們可以在聲明時直接賦值,也可以在靜態代碼塊中對其賦值,如果賦值的的靜態字段是被flnal所修飾過的,並且它的類型是基本類型或者是字符串時,那麼該字段便會被Java編譯器標記爲常量值,其初始化直接由Java虛擬機完成,除此之外的直接賦值操作,以及所有靜態代碼塊中的代碼,都會被Java編譯器置於一個叫<clinit>的方法中,爲了保證該方法只被執行一次,Java虛擬機會通過加鎖來確保該方法只被執行一個。

類的初始化會在以下8種情況下觸發:

       1.當虛擬機啓動時,初始化用戶指定的主類;

       2.當遇到用以新建目標類實例的new指令是,初始化new指令的目標類;

       3.當遇到調用靜態方法的指令時,初始化靜態方法所在的類;

       4.當遇到訪問靜態字段的指令時,初始化靜態字段所在的類;

       5.子類初始化會觸發父類的初始化;

       6.如果一個接口定義了default方法,那麼直接實現或者間接實現該接口的類的初始化,會觸發該接口的初始化;、

       7.使用反射API對某個類進行反射調用時,初始化這個類;

       8.當初次調用MethodHandle實例時,初始化該MethodHandle指向的方法所在的類;

 

 

     

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章