詳解類的加載過程

類加載主要職責就是加載各種 class 文件到 JVM 中,
類的加載過程主要分爲三個階段:加載、連接、初始化
其中連接又分爲:驗證、準備、解析三個過程
在這裏插入圖片描述

加載

類的加載就是將class文件中的二進制數據讀取到內存中,然後將該字節流鎖代表的靜態存儲結構轉換爲方法區中運行時的數據結構,並且在堆內存中生成一個該類的java.lang.Class 對象,作爲訪問方法區數據結構的入口。

連接

1. 驗證

驗證階段主要是確保class文件的字節流鎖包含的內容符合當前 jvm 規範的要求,並且不會出現危害 jvm 自身安全的代碼,如果不符合要求則會拋出 VerifyError 這樣的異常或其子異常

驗證信息

  • 文件格式
  • 元數據驗證
  • 字節碼驗證
  • 符號引用驗證

2. 準備

字節流通過驗證後,會爲該對象的類變量(也就是靜態變量)分配內存並且設置初始值,類變量的內存會被分配到方法區中,不同於實例變量會被分配到堆內存中
這裏的設置初始值就是爲類變量給定一個該類型的默認值
在這裏插入圖片描述
注意:final 修飾的靜態變量可以直接計算得出結果,不會導致類的初始化,是一種被動引用,所以不需要連接

3. 解析

解析會交叉一些驗證的過程,解析就是在常量池中尋找類、接口、字段和方法的符號引用,並且將這些符號引用替換成直接引用的過程。

  • 符號引用:
    符號引用以一組符號來描述所引用的目標, 符號可以是任何形式的字面量, 只要使用時能夠無歧義的定位到目標即可. 例如, 在Java中, 一個Java類將會編譯成一個class文件. 在編譯時, Java類並不知道所引用的類的實際地址, 因此只能使用符號引用來代替.
    舉個例子:org.simple.People類引用了org.simple.Language類, 在編譯時People類並不知道Language類的實際內存地址, 因此只能使用符號org.simple.Language來表示Language類的地址.

  • 直接引用:
    直接引用和虛擬機的佈局是相關的,不同的虛擬機對於相同的符號引用所翻譯出來的直接引用一般是不同的。如果有了直接引用,那麼直接引用的目標一定被加載到了內存中

初始化階段

初始化階段主要是給類變量中賦予代碼中定義的初始值而不是變量類型的默認值

JVM主動使用類的六種場景

  • 通過new 關鍵字會導致類的初始化

  • 訪問類的靜態變量,包括讀取和更新會導致類的初始化

public class test {
		static {
			System.out.println("I'mhere");
		}
		public static int x=10;
		
		public static void  main(String[]  args){
			System.out.println("ok");
		}
	}

x是簡單的靜態變量,其他類即使不對Simple進行new的創建,直接訪問變量x也會導致類的初始化

  • 訪問類的靜態方法,也會導致類的初始化
	public class test{
		static{
			System.out.println("I will be inisialized");
		}
		public static void a(){
		
		}
		public static void main(String[]args){
			System.out.println("ok");
		}
	}
  • 對某個類進行反射操作,會導致類的初始化
public static void main(String[]  args) throws  ClassNotFoundException{
	Class.forName("com.xinling.ToolsDemo.CountryEnum");
}

運行這段代碼也能看到靜態代碼塊中的輸出語句

  • 初始化子類會導致父類初始化
  • 啓動類:執行main函數所在的類會導致該類初始化

除了上述6種情況,其餘的都被稱爲被動使用,不會導致類的加載和初始化

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