所属文集:ClassLoader串烧
发起一个类的加载过程的类加载器和最终实际加载这个类的类加载器可能并不是一个。前者称为初始类加载器,而后者称为定义类加载器。两者的关联在于:一个 Java 类的定义类加载器是该类所导入的其它 Java 类的初始类加载器。比如类 A 通过 import 导入了类 B,那么由类 A 的定义类加载器负责启动类 B 的加载过程。
比如Person类中用到了String类型的字段。
AppClassLoader 是Person类的定义类加载器
是String的初始类加载器(委托bootStrapClassLoader去加载String类型)
下图中同时带有命名空间的知识,命名空相关的知识可以晚点通过ClassLoader串烧
和类加载器的命名空间 实例验证这两个文章去理解。
当然这个例子有问题,String类是JVM预加载的,可以把String类换成别的非预加载的,但是需要启动类加载器加载的类,如下文中提到的StrictMath。
测试验证,什么初始类加载器
VM参数中加上 : -XX:+TraceClassLoading
日志中可看到类的加载信息
1. 验证通过import 的方式做初始类加载器
验证逻辑:
1.1 AppClassLoader加载ClassDemo类,ClassDemo类中的方法使用了(import)了需要BootStrap加载的StrictMath的类.
1.2. new ClassDemo的实例并调用其方法,触发两个类的加载
1.3 调用AppClassLoader的loadClass方法,查看其findLoadedClass是否返回类信息,若返回了说明AppClassLoader是StrictMath的初始类
1.4 调用ExtClassLoader的loadClass方法,查看其findLoadedClass是否返回类信息,若没有返回了说明ExtClassLoader不是StrictMath的初始类实际结论:
Bootstrap ClassLoader 是StrictMath 的定义类加载器
AppClassLoader 是 StrictMath 的初始类加载器
ExtClassLoader 不是 StrictMath 的初始类加载器验证过程:
public class ClassDemo {
public int getMath() {
return StrictMath.abs(10);
}
}
@Test
public void testInitialCL1(){
try {
System.out.println("start");
ClassDemo classDemo = new ClassDemo();
System.out.println("ClassDemo classLoader :" + ClassDemo.class.getClassLoader());
classDemo.getMath();//此方法会触发StrictMath类的加载
System.out.println("------");
System.out.println(StrictMath.class.getClassLoader());
Class<?> strictMathClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.StrictMath");
System.out.println(strictMathClass.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
从日志中可以看出
ClassDemo 的类加载器是 AppClassLoader
classDemo.getMath(); 方法中使用了 StrictMath类,
触发了 StrictMath 类的加载;
start
ClassDemo classLoader :sun.misc.Launcher$AppClassLoader@18b4aac2
[Loaded java.lang.StrictMath from C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar]
------
null
null
[Loaded org.junit.runner.notification.RunNotifier$7 from file:/C:/Users/rock/.m2/repository/junit/junit/4.12/junit-4.12.jar]
[Loaded org.junit.runner.notification.RunNotifier$2 from file:/C:/Users/rock/.m2/repository/junit/junit/4.12/junit-4.12.jar]
[Loaded java.lang.Shutdown from C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar]
通过 AppClassLoader的 findLoadedClass 成功返回了StrictMath类
ExtClassLoader的 findLoadedClass 返回了null,不是StrictMath的初始类.
2. 通过ClassLoader#loadClass()加载类,发起加载的ClassLoader不是初始类加载器
@Test
public void testInitialCL(){
//查看GenericSignatureFormatError的初始类加载器
Class<?> StrictMath = null;
try {
System.out.println("start");
StrictMath = ClassLoader.getSystemClassLoader().loadClass("java.lang.StrictMath");
System.out.println(StrictMath.getClassLoader());
StrictMath = ClassLoader.getSystemClassLoader().loadClass("java.lang.StrictMath");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
从日志上可以看出
第一次loadClass,JVM执行了StrictMath 类的加载动作
且是有bootstrap classloader进行的加载。StrictMath.getClassLoader() = null;
start
[Loaded java.lang.StrictMath from C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar]
null
[Loaded java.security.Policy from C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar]
[Loaded java.security.Policy$UnsupportedEmptyCollection from C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar]
[Loaded java.util.concurrent.atomic.AtomicReference from C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar]
[Loaded java.security.Policy$PolicyInfo from C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar]
第二次loadClass, 观察断点信息,AppClassLoader 的findLoadedClass 返回的是null,
ExtClassLoader 的indLoadedClass 返回的是null,
bootStrapClassLoader 返回的不是null
证明了通过loadClass方式发起类加载的动作的classloader,不是初始类加载器
先loadClass 再Import是什么效果呢?
/**
* StrictMath 是boostrap类加载器加载的。
* loadClass的方式,AppClassLoader不是其初始类
* trictMath 是boostrap类加载器加载的。
* import的方式再使用,AppClassLoader是其初始类
*/
@Test
public void testInitialCL2(){
Class<?> StrictMath = null;
try {
System.out.println("start");
StrictMath = ClassLoader.getSystemClassLoader().loadClass("java.lang.StrictMath");
System.out.println(StrictMath.getClassLoader());
StrictMath = ClassLoader.getSystemClassLoader().loadClass("java.lang.StrictMath");
System.out.println("start2");
ClassDemo classDemo = new ClassDemo();
System.out.println("ClassDemo classLoader :" + ClassDemo.class.getClassLoader());
classDemo.getMath();//此方法会触发StrictMath类的加载
System.out.println("------");
System.out.println(StrictMath.class.getClassLoader());
Class<?> strictMathClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.StrictMath");
System.out.println(strictMathClass.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
结论
一个 Java 类的定义类加载器是该类所导入的其它 Java 类的初始类加载器
不同namespace的类是不可见的;除非显示地提供了允许它们进行交互的机制,如得到类所对应的Class对象的引用,使用反射来操作类. 案例1中已证明AppClassLoader不是初始类,其命名空间中没有StrictMath类,但是从bootstrap ClassLoader中拿到了StrictMath的类信息后,还可以对其进行操作.