JVM學習札記(二)~ 類加載器雙親委託機制

一、概述

類加載器用來把類加載到Java虛擬機中,從JDK 1.2開始,類的加載採用雙親委託機制,這種機制能更好的保證Java平臺的安全;在雙親委託機制中,除了Java虛擬機自帶的根類加載器以外,其餘的類加載都有且只有一個父加載器

在這裏插入圖片描述

當loader1加載Sample時,會委託給自己的父類加載器“系統類加載器”,“系統類加載器”會委託給“擴展類加載器”,“擴展類加載器”會委託給“根類加載器”,但是根類加載器嘗試加載之後,並不能完成加載,便返回給“擴展類加載器”,擴展類加載器也無法加載,便返回給“系統類加載器”,“系統類加載器”可以加載,便完成了加載過程

在這裏插入圖片描述

二、Java中的三種類加載器

1. Bootstrap ClassLoader(啓動類加載器)

負責加載JAVA_HOME中jre/lib/rt.jar裏所有的class,由c++實現,不是Classload子類

2. Extension ClassLoader(擴展類加載器)

負責加載Java平臺中擴展功能的一些jar包,包括AVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目錄下的jar包

3. App ClassLoader(系統類加載器)

負責加載classpath中的jar以及class

示例:獲取並打印類加載器的類加載路徑
/**
 * 查看打印不同的類加載器的加載路徑及文件
 * 1. 啓動類加載器 System.getProperty("sun.boot.class.path")
 * 2. 擴展類加載器 System.getProperty("java.ext.dirs")
 * 3. 系統/應用類加載器 System.getProperty("java.class.path")
 */
public class ClassLoader_03 {

    public static void main(String[] args) throws IOException {
        Arrays.asList(System.getProperty("sun.boot.class.path")
                .split(":"))
                .forEach(System.out::println);
        System.out.println("----------");
        Arrays.asList(System.getProperty("java.ext.dirs")
                .split(":"))
                .forEach(System.out::println);
        System.out.println("----------");
        Arrays.asList(System.getProperty("java.class.path")
                .split(":"))
                .forEach(System.out::println);

    }
}

輸出:

/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/resources.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/rt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/sunrsasign.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jsse.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jce.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/charsets.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfr.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/classes
----------
/Users/lizza/Library/Java/Extensions
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext
/Library/Java/Extensions
/Network/Library/Java/Extensions
/System/Library/Java/Extensions
/usr/lib/java
----------
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/charsets.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/deploy.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/cldrdata.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/dnsns.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jaccess.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jfxrt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/localedata.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/nashorn.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunec.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/zipfs.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/javaws.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jce.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfr.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfxswt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jsse.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/management-agent.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/plugin.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/resources.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/rt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/ant-javafx.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/dt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/javafx-mx.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/jconsole.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/packager.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/sa-jdi.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/tools.jar
/Users/lizza/Work/idea/learner/jvm/jvm_03_classloader/target/classes
/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
示例:驗證啓動類加載器加載自定義的class文件
/**
 * 驗證啓動類加載器加載自定義的class文件
 * 1. 將ClassLoader_01.class文件拷貝到啓動類加載器的加載路徑下:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/classes
 * 2. 運行程序驗證雙親委派機制
 */
public class ClassLoader_04 {

    public static void main(String[] args) throws Exception {
        CustomClassLoader loader = new CustomClassLoader("loader");
        Class<?> clazz = loader.loadClass("com.lizza.ClassLoader_01");
        System.out.println(clazz.hashCode());
        System.out.println(clazz.getClassLoader());

    }
}

輸出

1627674070
null

三、定義類加載器與初始類加載器

  • 如果有一個類加載器能夠成功的加載Test類,那這個類加載器稱爲定義類加載器,所有能返回Class對象引用的類加載器(包括定義類加載器)都被稱爲初始類加載器

四、不同類加載器命名空間的關係

  • 不同類加載器命名空間中的類是相互不可見的,同一個類加載器命名空間中的類是相互可見的
  • 由於自類加載器的命名空間包含父類加載器的命名空間,所以父類加載器加載的類在子類加載器的命名空間中是可見的;但是由子類加載器所加載的類對父類加載器是不可見的
  • 如果兩個類加載器之間沒有父子關係,那麼他們加載的類相互不可見
示例:不同的類加載器命名空間中類的可見性
/**
 * 不同類加載器的命名空間的類的可見性
 * 1. 刪除CLASSPATH下com.lizza.User.class文件
 * 2. 將CLASSPATH下com文件夾移至Desktop
 */
public class ClassLoader_07 {

    public static void main(String[] args) throws Exception {
        CustomClassLoader loader1 = new CustomClassLoader("loader1", "/Users/lizza/Desktop/");
        CustomClassLoader loader2 = new CustomClassLoader("loader2", "/Users/lizza/Desktop/");

        Class<?> clazz1 = loader1.loadClass("com.lizza.User");
        Class<?> clazz2 = loader2.loadClass("com.lizza.User");

        /**
         * 輸出false, 原因
         * 1. loader1和loader2在加載User類時都會委託給系統類加載器去加載,
         *    系統類加載器無法加載, 便一直向上委託給啓動類加載器加載器, 啓動類
         *    加載器無法加載, 委託給自定義類加載器
         * 2. 自定義類加載器嘗試加載並且可以成功加載, 但是loader1和loader2是兩個不同的類加載器
         *    故class對象不相同
         */
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1.getClassLoader());
        System.out.println(clazz2.getClassLoader());

        Object o1 = clazz1.newInstance();
        Object o2 = clazz2.newInstance();
        Method method = clazz1.getMethod("setUser", Object.class);
        method.invoke(o1, o2);

    }
}

五、雙親委託機制的優勢

  1. 保證Java核心類庫的安全: 雙親委託機制保證了Java的核心類庫只會由啓動類加載器加載, 不會存在 多個版本, 相互之間也是可見的
  2. 保證了核心類不會被自定義類所替代
  3. 不同的類加載器可以提供不同的命名空間, 相互之間相互隔離, 不同的類加載器加載的類相互之間不可見
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章