首先来了解几个概念:
类加载:
概念:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验--转换解析--初始化,最终形成能被java虚拟机直接使用的java类型,就是jvm的类加载机制。
类加载包含了以下过程:加载--验证--准备--解析--初始化--使用--卸载
类加载器:
首先,什么是类加载器?
概念:把类加载阶段中"通过一个类的全限定名来获取描述此类的二进制流"这个动作放到jvm外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块被称为"类加载器"。
分类:(针对hot spot而言,因为MRP,maxine的虚拟机完全是由java实现)
站在java虚拟机的角度上讲,只存在两种不同的类加载器。启动类加载器 和 其他类加载器。
启动类加载器:由c++实现,是虚拟机自身的一部分.
其他类加载器:由java实现,独立于虚拟机外部,全继承于抽象类java.lang.ClassLoader.
站在java开发人员的角度上讲 ,分为三种
*启动类加载器(BootStrap ClassLoader): 前面已经提到,主要加载<JAVA_HOME>/lib目录中 ,或被-Xbootclasspath参数指定路径中的jar包。
*扩展类加载器(Extension ClassLoader):由sun.misc.Launcher$ExtClassLoader来实现。主要加载<JAVA_HOME>/lib/ext 目录中 ,或被java.ext.dirs系统变量指定路径中所有类库。
*应用程序类加载器(Application ClassLoader):由sun.misc.Launcher$AppClassLoader来实现。由于这个类加载器是ClassLoader中getSystemClassLoader()方法返回值,所以也叫系统类加载器。负责加载用户类路径上(ClassPath)指定的类库,是开发过程中默认的加载器,开发者可直接使用。
说到这里,不得不提一下"双亲委派模型(Parent Delegation Model)"。如上图。
双亲委派模型:描述类加载器之间的层次关系的就是双亲委派模型。
双亲委派模型要求出顶层的 启动类加载器 外,其余类加载器都应有自己的父类加载器。
加载器之间的父子关系不是靠继承的关系实现,是使用组合关系的关系来复用父类加载器的代码。
双亲委派模型工作过程:若一个类加载器收到类加载请求,他首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器去完成,每一层类加载器都是如此,所以所有请求都会传送到顶层的BootStrap ClassLoader,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围没找到),子加载器才会自己尝试去加载。
使用双亲委派模型好处:
java类以及它的类加载器一起具备了一种带有优先级的层次关系。如 java.lang.Object(它在rt.jar之中)。
首先我们要知道一点,同一个类 被不同的 类加载器 加载成两个类之后,这两个类是不同的。
如果java.lang.Object类被不同的类加载器加载很多次,jvm中存在了多个不同的Object类,那么java类型体系中最基础的行为也无从保证,应用程序会一片混乱(we know,all the class extends from java.lang.Object,如果Object存在多个品种,那绝对是灾难)。
相反,使用了双亲委派模型,无论哪个类加载器去加载,都会委派到BoorStrap ClassLoader进行加载,保证了Object在各个类加载器环境中,都是同一个类。
再提一点双亲委派模型的工作机制,以类 A 为例,A如果以Application ClassLoader方式来加载,Application ClassLoader委派给Extension ClassLoader,再到BootStrap ClassLoader.
然而,BootStrap ClassLoader管理范围内(<JAVA_HOME>/lib目录中 ,或-Xbootclasspath参数指定路径中的jar包)没查到类A,那么就让最初请求的类加载器,Application ClassLoader来加载
at last,提一点,双亲委派模型 在jvm成长史上被重大破坏过三次,但是 双亲委派模型确实不错,还是像小强一样的活了下来