JVM基礎篇——雙親委派機制和沙箱安全機制

雙親委派機制

雙親委派機制

Java虛擬機對class文件採用的是按需加載的方式,也就是說當需要使用該類時纔會將它的class文件加載到內存生成class對象。而且加載某個類的class文件時,Java虛擬機採用的是雙親委派模式,即把請求交由收類處理,它是一種任務委派模式。

我們現在想出一個問題,如果我們也一個java.lang.String類,那麼進行類加載的時候加載是哪一個類呢??

先寫段代碼

# 自定義String類
package java.lang;

public class String {

    static {
        System.out.println("自定義String");
    }

    public static void main(String[] args) {
        System.out.println("hello , world");
    }
}

# String測試類  StringTest.java
public class StringTest {
    public static void main(String[] args) {
        java.lang.String string = new java.lang.String();
    }
}

測試的結果是什麼都不輸出,所以說明使用的是核心api的Stribg類

爲什麼會出現這樣的結果呢??那麼進入咱們今天的主題——雙親委派機制

雙親委派機制工作原理

(1)如果一個類加載器收到了類加載請求,它並不會自己先去加載,而是把這個請求委託給父類的加載器去執行
(2)如果父類加載器還存在其父類加載器,則進一步向上委託,依次遞歸,請求最終將到達項層的啓動類加載器
(3)如果父類加載器可以完成類加載任務,就成功返回,倘若父類加載器無法完成此加載任務,子加載器纔會嘗試自己去加載,這就是雙親委派模式

那麼剛纔那個類的加載流程是什麼樣的呢??

流程圖理解:
在這裏插入圖片描述

首先走的是系統類加載器,之後發現還有父類,去找擴展類加載器,發現還有父類,去找引導類加載器。如果說最上面的引導類加載器不負責這個類的加載,那麼擴展類加載器負責加載,如果擴展類加載器不負責加載,那麼系統類(應用程序類)加載器負責加載。

之前的類引導子系統提到過:引導類加載器加載的是Java核心類庫JAVA_HOME/jre/lib/rt,jar、resources.jar、sun.boot.class.path路徑下的內容,只加載包名是java, javax, sun開頭類。
那麼引導類加載器負責java.lang包下類的加載,所以加載的是java核心類庫的String類。

雙親委派機制的優點
  • 防止重複加載同一個.class。通過委託去向上面問一問,加載過了,就不用再加載一遍。保證數據安全。
  • 保證核心.class不能被篡改。通過委託方式,不會去篡改核心.clas,即使篡改也不會去加載,即使加載也不會是同一個.class對象了。不同的加載器加載同一個.class也不是同一個Class對象。這樣保證了Class執行安全。

看下面這段代碼:

# 自定義java.lang.String類
package java.lang;
public class String {
    static {
        System.out.println("自定義String");
    }
}
# 自定義java.lang.StringTest類
package java.lang;
public class StringTest {
    public static void main(String[] args) {
        String string = new String();
    }
}

# 執行時期拋出異常
java.lang.SecurityException: Prohibited package name: java.lang

在私自創建java類的時候,可能和已經存在的類發生衝突,甚至對程序產生損害,Java採取雙親委派機制,保證類只是加載一次,可以避免類的重複加載,保證程序的安全。

沙箱安全機制

自定義String類,在加載自定義String類的時候會率先使用引導類加載器加載,而引導類加載器在加載的過程中會先加載jdk自帶的文件。(rt. jar包中java\lang\String. class),報錯信息說沒有main方法p就是因爲加載的是rt. jar包中的String類。這樣可以保證對java核心源代碼的保護,這就是沙箱安全機制。

其他的類加載器知識點補充

對類加載器的引用

JVM必須知道–個類型是由啓動加載器加載的還是由用戶類加載器加載的。如果一個類型是由用戶類加載器加載的,那麼JVM會將這。個類加載器的-一個引用作爲類型信息的一部分保存 在方法區中q當解析一個類型到另一個類型的引用的時候,JVM需 要保證這兩個類型的類加載器是相同的。

在JVM中表示兩個class對象是否爲同-一個類存在兩個必要條件:

  • 類的完整類名必須一致,包括包名。
  • 加載這個類的ClassLoader (指ClassLoader實例對象)必須相同。

換句話說,在JVM中,即使這兩個類對象(class對象)來源同-一個Class文件,被同一個虛擬機所加載,但只要加載它們的ClassLoader實例對象不同,那麼這兩個類對象也是不相等的。

類的主動使用和被動使用

Java程序對類的使用方式分爲:主動使用和被動使用。

  • 主動使用,又分爲七種情況:
    • 創建類的實例
    • 訪問某個類或接口的靜態變量,或者對該靜態變量賦值
    • 調用類的靜態方法
    • 反射(比如: Class . forName (“com. atguigu . Test”) )
    • 初始化一個類的子類
    • Java虛擬機啓動時被標明爲啓動類的類
    • JDK 7開始提供的動態語言支持:
      java.lang.invoke.MethodHandle實例的解析結果
      REF_getStatic、REF_putStatic、REF_ invokeStatic句柄對應的類沒有初始化,則初始化
  • 除了以上七種情況,其他使用Java類的方式都被看作是對類的被動使用,都不會導致類的初始化。

兩種類的加載機制就介紹到這兒,謝謝你可以看到最後。Thank You!!!

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