本文首发于 www.yidooo.net/2017/06/11/… 转载请注明出处html
一个Java程序是由若干个class文件组成。当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不一样的class文件当中,因此常常要从这个class文件中要调用另一个class文件中的方法,若是另一个class文件不存在的,则会引起ClassNotFoundException。java
程序在启动的时候,并不会一次性加载程序所要用的全部class文件,而是根据程序的须要,经过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存以后,才能被其它class所引用。因此ClassLoader就是用来动态加载class文件到内存当中用的。web
如今先看下ClassLoader的体系结构: 算法
BootStrap ClassLoader:称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等。api
能够经过以下程序得到该classloader所加载的jar缓存
System.out.println(System.getProperty("sun.boot.class.path"));
复制代码
程序输出:tomcat
/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/classes
复制代码
若是是scala会在java的基础上额外加载几个jar。安全
scala> println(System.getProperty("sun.boot.class.path"))
/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/classes:/Users/zhenlong/DevTools/scala/lib/akka-actor_2.11-2.3.10.jar:/Users/zhenlong/DevTools/scala/lib/config-1.2.1.jar:/Users/zhenlong/DevTools/scala/lib/jline-2.12.1.jar:/Users/zhenlong/DevTools/scala/lib/scala-actors-2.11.0.jar:/Users/zhenlong/DevTools/scala/lib/scala-actors-migration_2.11-1.1.0.jar:/Users/zhenlong/DevTools/scala/lib/scala-compiler.jar:/Users/zhenlong/DevTools/scala/lib/scala-continuations-library_2.11-1.0.2.jar:/Users/zhenlong/DevTools/scala/lib/scala-continuations-plugin_2.11.7-1.0.2.jar:/Users/zhenlong/DevTools/scala/lib/scala-library.jar:/Users/zhenlong/DevTools/scala/lib/scala-parser-combinators_2.11-1.0.4.jar:/Users/zhenlong/DevTools/scala/lib/scala-reflect.jar:/Users/zhenlong/DevTools/scala/lib/scala-swing_2.11-1.0.2.jar:/Users/zhenlong/DevTools/scala/lib/scala-xml_2.11-1.0.4.jar:/Users/zhenlong/DevTools/scala/lib/scalap-2.11.7.jar
复制代码
Extension ClassLoader:称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的全部jar。该类在sun.misc.launcher包中。bash
App ClassLoader:称为应用类加载器,也称为System ClassLoaer,负责加载应用程序CLASSPATH下的全部jar和class文件。该类也在sun.misc.launcher包中。服务器
用户还能够根据须要定义自已的ClassLoader,而这些自定义的ClassLoader都必须继承自java.lang.ClassLoader类.
全部的ClassLoader都必须继承自java.lang.ClassLoader,而Bootstrap ClassLoader却不是。它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
ClassLoader使用的是双亲委托模型来搜索类的,每一个ClassLoader实例都**包含(而不是继承)**一个父类加载器的引用。虚拟机内置的类加载器(Bootstrap ClassLoader)自己没有父类加载器,但能够用做其它ClassLoader实例的的父类加载器。
采用双亲委托机制加载类的时候采用以下的几个步骤:
总之,class的加载顺序为:cache -> parent classloader -> self.
使用双亲委托模型的好处:
在JVM在搜索类的时候,又是如何断定两个class是相同的呢?**JVM在断定两个class是否相同时,不只要判断两个类名是否相同,并且要判断是否由同一个类加载器实例加载的。只有二者同时知足的状况下,JVM才认为这两个class是相同的。**就算两个class是同一份class字节码,若是被两个不一样的ClassLoader实例所加载,JVM也会认为它们是两个不一样class。好比有一个Java类SimpleClass.java,javac编译以后生成字节码文件SimpleClass.class,ClassLoaderA和ClassLoaderB这两个类加载器并读取了SimpleClass.class文件,并分别定义出了java.lang.Class实例来表示这个类,对于JVM来讲,它们是两个不一样的实例对象,但它们确实是同一份字节码文件,若是试图将这个Class实例生成具体的对象进行转换时,就会抛运行时异常java.lang.ClassCaseException,提示这是两个不一样的类型。
下面是一个自定义ClassLoader的例子:
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = this.findLoadedClass(name); // optional
if (clazz == null) {
byte[] classData = loadClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
clazz = defineClass(name, classData, 0, classData.length);
}
return clazz;
}
private byte[] loadClassData(String name) {
// TODO: load class data.
return null;
}
}
复制代码