JVM —— 類加載器子系統

類加載的過程

image.png

  1. 加載
    1. 通過一個類的全限定名獲取定義此類的二進制字節流
    2. 將這個字節流所代表的靜態存儲結構轉化爲方法去運行時的數據結構
    3. 在內存中生成一個代表這個類的 java.lang.Class 對象,作爲方法區這個類的各種數據的訪問入口;
  2. 鏈接
    1. 驗證        目的在於確保 Class 文件的字節流中包含信息符合當前虛擬機的要求,保證被加載類的正確性,不會危害虛擬機自身安全
    2. 準備        爲類變量分配內存並且設置該類變量的默認初始值,即零值。這裏不包含用 final 修飾的 static,因爲 final 編譯的時候就會分配了,準備階段會顯式初始化;這裏不會爲實例變量分配初始化,類變量分配在方法區中,而實例變量是會隨着對象一起分配到 Java 堆中;
    3. 解析        將常量池內的符號引用轉換爲直接引用的類型;解析操作往往會伴隨着 JVM 在執行完之後再執行;
  3. 初始化        初始化階段就是執行類構造器方法<clinit>()的過程;此方法不需定義,是 javac 編譯器自動收集類中的所有類變量的賦值動作和靜態代碼塊中的語句合併而來;指令按照語句在原文件中的順序執行。<clinit>()不同於類的構造器。若該類有父類,JVM會保證子類的<clinit>()執行之前,父類的<clinit>()已經執行完畢。虛擬機必須保證一個類的<clinit>()方法在多線程下唄同步加鎖。

雙親委派機制

  • 什麼是雙親委派機制?
    • 當一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父類去完成,每一個層次類加載器都是如此,因此所有的加載請求都應該傳送到啓動類加載器中,只有當父類加載器反饋自己無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的 Class),子類加載器纔會嘗試自己去加載。
    • 例如當我們自己在 java.lang 包下寫一個String類,然後在別的類裏面 new String(); 執行過程是:系統類加載器接到加載請求,但是不進行加載,先給其父類的擴展類加載器,擴展類加載器也同樣委派給啓動類加載器,啓動類加載器收到之後查路徑,會查到系統的String類,所以最後加載的是系統的String類,而不是我們自己定義的。
  • 優點
    • 避免類的重複加載
    • 保護程序安全,防止核心API被隨意篡改

沙箱安全機制

雙親委派中不會加載自定義的 String 類,而是加載了系統的 String 類,這樣可以保護對Java核心源代碼的保護,這就是沙箱安全機制。

其他補充

  • JVM中表示兩個 class 對象是否爲同一個類存在兩個必要條件:
    • 類的完整類名必須一致,包括包名;
    • 加載這個類的 classLoader (指 ClassLoader 實例對象)必須相同。 
  • 類的主動使用,類的初始化的七種情況:
    • 創建類的實例;
    • 訪問某個類或接口的靜態變量,或者對該靜態變量賦值;
    • 調用類的靜態靜態方法;
    • 反射(比如;Class.forName("com.atguigu.Test"));
    • 初始化一個類的子類;
    • Java虛擬機啓動時被註明爲啓動類的類;
    • JDK 7 開始提供的當他語言支持;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章