09_JVM学习笔记_启动类加载器与自定义系统类加载器详解

启动类加载器与自定义系统类加载器

示例代码

public class MyTest23 {

    public static void main(String[] args) {
        System.out.println(System.getProperty("sun.boot.class.path"));
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println(System.getProperty("java.class.path"));
    }
}

运行输出

/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/classes
/Users/leofight/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.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_66.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/tools.jar:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar

修改sun.boot.class.path的路径
执行java -Dsun.boot.class.path=./ com.leofight.jvm.classloader.MyTest23输出

Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object

在Oracle的Hotspot实现中,系统属性sun.boot.class.path如果修改错了,则运行会出错,提示如下错误信息:

 Error occurred during initialization of VM
 java/lang/NoClassDefFoundError: java/lang/Object

内建于JVM中的启动类加载器会加载java.lang.ClassLoader以及其他的Java平台类,当JVM启动时,一块特殊的机器码会运行,它会加载扩展类加载器与系统类加载器,这块特殊的机器码叫做启动类加载器(Bootstrap)。

启动类加载器并不是Java类,而其他的加载器则都是Java类,启动类加载器是特定于平台的机器指令,它负责开启整个加载过程。

所有类加载器(除了启动类加载器)都被实现为Java类,不过,总归要有一个组件来加载第一个Java类加载器,从而让整个加载过程能够顺利进行下去,加载第一个纯Java类加载器就是启动类加载器的职责。

启动类加载器还会负责提供JRE正常运行所需要的基本组件,这包括java.util与java.lang包中的类等等。

执行System.out.println(ClassLoader.class.getClassLoader());输出
null

执行System.out.println(Launcher.class.getClassLoader());输出null

扩展类加载器与系统类加载器也是由启动类加载器所加载的。

/*
返回委托的系统类加载器,这是新的ClassLoader实例的默认委派父亲,
通常是用于启动应用程序的类加载器
*/
/*
该方法在运行时的启动序列中首先被调用,此时它创建系统类加载器并将其设置为调用
Thread的上下文类加载器。
*/
//默认的系统类加载器是一个实现依赖于这个类的实例。
/*
如果系统属性`java.system.class.loader`被定义当这个方法第一次被调用那么该属性的值
将被视为将作为系统类加载器返回的类的名称。该类使用默认的系统类加载器进行加载,并且必须
定义一个公共构造函数,该构造函数接受用作委派父级的 ClassLoader的单个参数。然后使用此
构造函数创建一个实例,并使用默认的系统类加载器作为参数。由此产生的类加载器被定义为系统类
加载器。
*/
 public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
  }

使用自定义类加载器
执行java -Djava.system.class.loader=com.leofight.jvm.classloader.MyTest16 com.leofight.jvm.classloader.MyTest23则输出

Error occurred during initialization of VM
java.lang.Error: java.lang.NoSuchMethodException: com.leofight.jvm.classloader.MyTest16.(java.lang.ClassLoader)
        at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1466)
        at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1433)
Caused by: java.lang.NoSuchMethodException: com.leofight.jvm.classloader.MyTest16.(java.lang.ClassLoader)
        at java.lang.Class.getConstructor0(Class.java:3082)
        at java.lang.Class.getDeclaredConstructor(Class.java:2178)
        at java.lang.SystemClassLoaderAction.run(ClassLoader.java:2207)
        at java.lang.SystemClassLoaderAction.run(ClassLoader.java:2192)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1453)
        at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1433)

为什么会报错呢?
如果使用自定义类加载器作为系统类加载器,则需要定义一个公共构造函数,该构造函数接受用作委派父级的 ClassLoader的单个参数。

在MyTest16中增加构造方法

 public MyTest16(ClassLoader parent){
        super(parent);
    }

然后再次执行java -Djava.system.class.loader=com.leofight.jvm.classloader.MyTest16 com.leofight.jvm.classloader.MyTest23则正常输出

在IDEA中运行System.out.println(System.getProperty("java.system.class.loader"));则输出null
在命令运行java -Djava.system.class.loader=com.leofight.jvm.classloader.MyTest16 com.leofight.jvm.classloader.MyTest23则输出com.leofight.jvm.classloader.MyTest16

在IDEA中运行

 System.out.println(MyTest23.class.getClassLoader());

 System.out.println(MyTest16.class.getClassLoader());

System.out.println(ClassLoader.getSystemClassLoader());

则,输出

sun.misc.Launcher$AppClassLoader@14dad5dc
sun.misc.Launcher$AppClassLoader@14dad5dc
sun.misc.Launcher$AppClassLoader@14dad5dc

在命令窗运行java -Djava.system.class.loader=com.leofight.jvm.classloader.MyTest16 com.leofight.jvm.classloader.MyTest23则输出

sun.misc.Launcher$AppClassLoader@14dad5dc
sun.misc.Launcher$AppClassLoader@14dad5dc
[class com.leofight.jvm.classloader.MyTest16@2018699554]

你可能感兴趣的:(09_JVM学习笔记_启动类加载器与自定义系统类加载器详解)