一、什么是classloader
二、Java原生的classloader
- BootStrap Classloader:引导类加载器,又称启动类加载器,是最顶层的类加载器,主要用来加载Java核心类,如rt.jar、resources.jar、charsets.jar等,
Sun的JVM中,执行java的命令中使用-Xbootclasspath选项或使用- D选项指定sun.boot.class.path系统属性值可以指定附加的类,它不是 java.lang.ClassLoader的子类,而是由JVM自身实现的该类c 语言实现,Java程序访问不到该加载器。通过下面代码可以查看该加载器加载了哪些jar包file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/classes,写到这里大家应该都知道,我们并没有在classpath里面指定这些类的路径,为啥还是能被加载到jvm并使用起来了吧,因为这些是bootstarp来加载的。
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));
输出结果则为用户在系统属性里面设置的CLASSPATH。
所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入;
委托机制则是先让parent类加载器寻找,只有在parent找不到的时候才从自己的类路径中去寻找。此外类加载还采用了cache机制,也就是如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因(但是通过使用新的classloader也可以是修改后的类生效不重启)。
三、类加载器原理
四、例子
String classloader:null
AESKeyGenerator class load:sun.misc.Launcher$ExtClassLoader
ABC classload:sun.misc.Launcher$AppClassLoader
ABC classload:sun.misc.Launcher$ExtClassLoader
String类是Java核心类,该类是由bootstarp classloader加载,而它是有c语言实现Java类中不可访问,所以这里输入null。 AESKeyGenerator类是ext下的一个类,可以该类是由ext classloader加载,它的父加载器为bootstarp,而自定义类Hello则使用了appclassloader来加载,而applcassloader的附加在其是extclassloder,有双亲委派可以知道加载hello类时候,appclassloader委托了extclassloader去加载,而extclassloader委托给bootstarp加载,bootstarp找不到该类,所以返回到extclassloader去ext 路径查找,查找不到有返回为appclassloader查找,appclasloader则在classpath中找到了该类,装入虚拟器运行。在这里我们可以在验证一下双亲委派模型的安全性,我们可以自己写一个String类,包路径和rt.jar里面的一模一样,看看程序是否会加载我们自己写的String类。
测试类
String classloader:null
MyString classloader:sun.misc.Launcher$AppClassLoader@36c51089
五、从源码看classloader双亲委派模型
protected Class<?> loadClass(Stringname,boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c ==null) {
longt0 = System.nanoTime();
try {
if (parent !=null) {
c = parent.loadClass(name,false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundExceptione) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c ==null) {
// If still not found, then invoke findClass in order
// to find the class.
longt1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 -t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
returnc;
}
}
(1)首先看jvm缓存里面是否有该类,有的话直接返回,否者执行(2)类加载器的顺序是:
先是bootstrap classloader,然后是extension classloader,最后才是system classloader。大家会发现加载的Class越是重要的越在靠前面。这样做的原因是出于安全性的考虑