1. 程式人生 > 實用技巧 >JVM-類載入器

JVM-類載入器

類載入系統

Java虛擬機器在執行Java程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。這些區域有各自的用途,以及建立和銷燬的時間,有的區域隨著虛擬機器程序的啟動而一直存在,有些區域則是依賴使用者執行緒的啟動和結束而建立和銷燬。根據《Java虛擬機器規範》的規定,Java虛擬機器所管理的記憶體將會包括以下幾個執行時資料區域,如圖所示。

類載入器

類載入子系統作用

類載入子系統負責從檔案系統或者網路中載入class檔案,class檔案在檔案開頭有特定的檔案標識即16進位制CA FE BA BE;載入後的Class類資訊存放於一塊成為方法區的記憶體空間。除了類資訊之外,方法區還會存放執行時常量池資訊,可能還包括字串字面量和數字常量ClassLoader只負責class檔案的載入,至於它是否可以執行,則由Execution Engine決定

功能細分

載入模組

  • 通過一個類的全限定明獲取定義此類的二進位制位元組流;
  • 將這個位元組流所代表的的靜態儲存結構轉化為方法區的執行時資料;
  • 在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的訪問入口

連結模組

  • 驗證(保證位元組流中包含資訊符合虛擬機器要求,檔案格式、源資料、位元組碼、符號引用)
  • 準備(類變數的分配記憶體及初始化,final修飾的靜態變數早已分配,例項的變數會在堆中分配)
  • 解析(常量池中的符號引用轉換為直接引用的過程,針對類、介面、欄位、類方法、介面方法、方法型別等)

初始化模組

就是執行類構造器方法clinit()的過程,只有靜態變數存在時才會有,按照在原始檔中出現的順序執行且父類的clinit先執行,在多執行緒情況下一個類的clinit方法被同步加鎖


類載入器分類

引導類載入器

java核心類庫都是使用引導類載入器BootStrapClassLoader載入的;並不繼承自java.lang.ClassLoader,沒有父載入器;載入拓展類和應用程式類載入器,並指定為他們的父載入器;BootStrap啟動類載入器只加載包名為java、javax、sun等開頭的類

自定義類載入器

所有派生於抽象類ClassLoader的類載入器都劃分為自定義類載入器
其中使用者自定義類載入器,可以隔離載入類、修改類載入的方式、拓展載入源、防止原始碼洩露

  • 拓展類載入器
    從java.ext.dirs系統屬性所指定的目錄中載入類庫,或從JDK的安裝目錄的jre/lib/ext子目錄(擴充套件目錄)下載入類庫。如果使用者建立的JAR放在此目錄下,也會由拓展類載入器自動載入
  • 應用程式類載入器
    它負責載入環境變數classpath或系統屬性 java.class.path指定路徑下的類庫該類載入器是程式中預設的類載入器。一般來說,java應用的類都是由它來完成載入;通過ClassLoader#getSystemClassLoader()方法可以獲取到該類載入器。
/**
 * 虛擬機器自帶載入器
 */
public class ClassLoaderTest1 {
	public static void main(String[] args) {
		System.out.println("********啟動類載入器*********");
		URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
		//獲取BootStrapClassLoader能夠載入的api路徑
		for (URL e:urls){
			System.out.println(e.toExternalForm());
		}
		//從上面的路徑中隨意選擇一個類 看看他的類載入器是什麼
		//Provider位於 /jdk1.8.0_171.jdk/Contents/Home/jre/lib/jsse.jar 下,引導類載入器載入它
		ClassLoader classLoader = Provider.class.getClassLoader();
		System.out.println(classLoader);
		//null
		System.out.println("********拓展類載入器********");
		String extDirs = System.getProperty("java.ext.dirs");
		for (String path : extDirs.split(";")){
			System.out.println(path);
		}
		//從上面的路徑中隨意選擇一個類 看看他的類載入器是什麼:拓展類載入器
		ClassLoader classLoader1 = CurveDB.class.getClassLoader();
		System.out.println(classLoader1);
		//sun.misc.Launcher$ExtClassLoader@4dc63996
	}
}
  • 換句話說,在jvm中,即使這兩個類物件(class物件)來源同一個Class檔案,被同一個虛擬機器所載入,但只要載入它們的ClassLoader例項物件不同,那麼這兩個類物件也是不相等的.

classloader的常用方法及獲取方式


雙親委派機制

如果一個類載入器受到類載入請求,不會自己先去載入,而是把請求委託給父類載入器執行,直到頂層的啟動類載入器,先父後子;
優勢在於:
避免類的重複載入、保護程式安全,防止核心API被隨意篡改

沙箱安全機制

Java安全模型的核心就是Java沙箱(sandbox),什麼是沙箱?沙箱是一個限制程式執行的環境。沙箱機制就是將 Java 程式碼限定在虛擬機器(JVM)特定的執行範圍中,並且嚴格限制程式碼對本地系統資源訪問,通過這樣的措施來保證對程式碼的有效隔離,防止對本地系統造成破壞。沙箱主要限制系統資源訪問,那系統資源包括什麼?——CPU、記憶體、檔案系統、網路。不同級別的沙箱對這些資源訪問的限制也可以不一樣。
詳情連結:

類的主動使用和被動使用

主動使用,分為七種情況

  • 建立類的例項

  • 訪問某各類或介面的靜態變數,或者對靜態變數賦值

  • 呼叫類的靜態方法反射 比如Class.forName(com.dsh.jvm.xxx)

  • 初始化一個類的子類

  • java虛擬機器啟動時被標明為啟動類的類

  • JDK 7 開始提供的動態語言支援:
    java.lang.invoke.MethodHandle例項的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic控制代碼對應的類沒有初始化,則初始化

  • 除了以上七種情況,其他使用java類的方式都被看作是對類的被動使用,都不會導致類的初始化。