启动类加载器与自定义系统类加载器
示例代码
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.<init>(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.<init>(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]