類加載的過程
類從被加載到虛拟機記憶體中開始,到解除安裝出記憶體為止,它的整個生命周期包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和解除安裝(Unloading)7個階段。其中驗證、準備、解析3個部分統稱為連接配接(Linking),類的聲明周期如下:

image.png
虛拟機規範嚴格規定了有且隻有以下5種情況必須立即對類進行初始化:
1)遇到new、getstatic、putstatic或invokestatic這4條位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這4條指令的場景有:使用new關鍵字執行個體化對象、讀取或設定一個類的靜态字段(被final修飾、已在編譯期把結果放入常量池的靜态字段除外),以及調用一個類的靜态方法的時候。
2)使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要觸發其初始化。
3)當初始化一個類的時候,如果發現其父類還沒有初始化,則需要先觸發其父類的初始化。
4)當虛拟機啟動時,使用者需要指定一個要執行的主類(包含main方法的那個類),虛拟機會先初始化這個類
5)當使用JDK1.7的動态語言支援時,如果一個java.lang.invoke.MethodHandle執行個體最後的解析結果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個方法句柄所對應的類沒有進行過初始化,則需要先觸發其初始化。
加載
加載是類加載的的一個階段。加載階段的主要步驟:
1)通過一個類的全限定名來擷取定義此類的二進制位元組流(不限于本地jar包)。
2)将這個位元組流所代表的靜态存儲結構轉化為方法區的運作時資料結構。
3)在記憶體中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種資料的通路入口。
認證
認證包括以下幾部分的認證:
1)檔案格式驗證:包括魔數、支援版本、常量類型、指向常量的索引的有效性、常量的編碼等
2)中繼資料驗證:類是否有父類、繼承的合法性、非抽象類方法的完整性、方法合理性等
3)位元組碼驗證:操作數棧語義合法性檢查、虛拟機規定的類型轉換校驗等
4)符号引用驗證:對類自身以外(常量池中的各種符号引用)的資訊進行比對性校驗,包括類、方法、字段等的描述符
準備
準備階段是正式為類變量配置設定記憶體并設定類變量(差別于執行個體變量,指的是類的靜态字段等)初始值的階段。比如int、short、char、float、long等類型的初始值是0,boolean類型初始值是false,reference對象類型的初始值是null。
解析
解析階段是虛拟機将常量池内的符号引用(Class以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_InvokeDynamic_info7種類符号引用結構描述)替換為直接引用(直接指向目标的指針、相對偏移量或一個能間接定位到目标的句柄)的過程。隻需要保證使用的符号使用之前被解析就可以,可以先于初始化。
除了與invokedynamic指令相關的類,其他類被解析後都會被緩存,避免解析動作的重複進行。
初始化
類初始化階段是類加載的最後一個階段。前面的類加載過程中,除了在加載階段使用者應用程式可以通過自定義類加載器參與之外,其餘動作完全由虛拟機主導和控制。到了初始化階段,才真正開始執行類中定義的java代碼。
初始化階段是執行類構造器<clinit>()方法的過程。<clinit>()方法是由編譯器自動收集所有類變量的指派動作和靜态語句塊(static{}塊)中的語句合并産生的,編譯器收集的順序是由語句在源檔案中出現的順序所決定的,它不需要顯示地調用父類構造器。
類加載器
對于任何一個類,都需要由加載它的類加載器和這個類本身一同确立其在java虛拟機中的唯一性,每一個類加載器,都擁有一個獨立的類名稱空間。不同類加載器加載的同一個類不相等。
虛拟機系統提供3種的類加載器:Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader
Bootstrap ClassLoader
啟動類加載器,一般由C++實作,是虛拟機的一部分。該類加載器主要職責是将JAVA_HOME路徑下的\lib目錄中能被虛拟機識别的類庫(比如rt.jar)加載到虛拟機記憶體中。Java程式無法直接引用該類加載器
Extension ClassLoader
擴充類加載器,由Java實作,獨立于虛拟機的外部。該類加載器主要職責将JAVA_HOME路徑下的\lib\ext目錄中的所有類庫,開發者可直接使用擴充類加載器。 該加載器是由sun.misc.Launcher$ExtClassLoader實作。
Application ClassLoader
應用程式類加載器,該加載器是由sun.misc.Launcher$AppClassLoader實作,該類加載器負責加載使用者類路徑上所指定的類庫。開發者可通過ClassLoader.getSystemClassLoader()方法直接擷取,故又稱為系統類加載器。當應用程式沒有自定義類加載器時,預設采用該類加載器。
雙親委派模型
如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父加載器去完成,每一個層次的類加載器都是如此。隻有當父加載器回報自己無法完成這個加載請求時,子加載器才會嘗試自己去加載。