在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有:

1、通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件)。而获取的方式,可以通过jar包、war包、网络中获取、JSP文件生成等方式。

2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。这里只是转化了数据结构,并未合并数据。(方法区就是用来存放已被加载的类信息,常量,静态变量,编译后的代码的运行时内存区域)

3、在内存中生成一个代表这个类的Java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象并没有规定是在Java堆内存中,它比较特殊,虽为对象,但存放在方法区中。

其中,实现第一个工作的代码块就被称为“类加载器”。

类加载器的作用不仅仅是实现类的加载,它还与类的的“相等”判定有关,关系着Java“相等”判定方法的返回结果,只有在满足如下三个类“相等”判定条件,才能判定两个类相等。

1、两个类来自同一个Class文件

2、两个类是由同一个虚拟机加载

3、两个类是由同一个类加载器加载

Java“相等”判定相关方法:

1、判断两个实例对象的引用是否指向内存中同一个实例对象,使用 Class对象的equals()方法,obj1.equals(obj2);
2、判断实例对象是否为某个类、接口或其子类、子接口的实例对象,使用Class对象的isInstance()方法,class.isInstance(obj);

3、判断实例对象是否为某个类、接口的实例,使用instanceof关键字,obj instanceof class;
4、判断一个类是否为另一个类本身或其子类、子接口,可以使用Class对象的isAssignableFrom()方法,class1.isAssignableFrom(class2)。

JVM类加载器分类详解:

1、Bootstrap ClassLoader:启动类加载器,也叫根类加载器,它负责加载Java的核心类库,加载如(%JAVA_HOME%/lib)目录下的rt.jar(包含System、String这样的核心类)这样的核心类库。根类加载器非常特殊,它不是java.lang.ClassLoader的子类,它是JVM自身内部由C/C++实现的,并不是Java实现的。

2、Extension ClassLoader:扩展类加载器,它负责加载扩展目录(%JAVA_HOME%/jre/lib/ext)下的jar包,用户可以把自己开发的类打包成jar包放在这个目录下即可扩展核心类以外的新功能。

3、System ClassLoader\APP ClassLoader:系统类加载器或称为应用程序类加载器,是加载CLASSPATH环境变量所指定的jar包与类路径。一般来说,用户自定义的类就是由APP ClassLoader加载的。

各种类加载器间关系:以组合关系复用父类加载器的父子关系,注意,这里的父子关系并不是以继承关系实现的。

  1. //验证类加载器与类加载器间的父子关系
  2. public static void main(String[] args) throws Exception{
  3. //获取系统/应用类加载器
  4. ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
  5. System.out.println("系统/应用类加载器:" + appClassLoader);
  6. //获取系统/应用类加载器的父类加载器,得到扩展类加载器
  7. ClassLoader extcClassLoader = appClassLoader.getParent();
  8. System.out.println("扩展类加载器" + extcClassLoader);
  9. System.out.println("扩展类加载器的加载路径:" + System.getProperty("java.ext.dirs"));
  10. //获取扩展类加载器的父加载器,但因根类加载器并不是用Java实现的所以不能获取
  11. System.out.println("扩展类的父类加载器:" + extcClassLoader.getParent());
  12. }
  13. }

类加载器的双亲委派加载机制(重点):当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

这个过程如下图标号过程所示:

双亲委派模型的源码实现:

主要体现在ClassLoader的loadClass()方法中,思路很简单:先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父类加载器为空则默认使用启动类加载器作为父类加载器。如果父类加载器加载失败,抛出ClassNotFoundException异常后,调用自己的findClass()方法进行加载。

  1. public Class<?> loadClass(String name) throws ClassNotFoundException {
  2. return loadClass(name, false);
  3. }
  4. protected synchronized Class<?> loadClass(String name, boolean resolve)
  5. throws ClassNotFoundException
  6. {
  7. // First, check if the class has already been loaded
  8. Class c = findLoadedClass(name);
  9. if (c == null) {
  10. try {
  11. if (parent != null) {
  12. c = parent.loadClass(name, false);
  13. } else {
  14. c = findBootstrapClassOrNull(name);
  15. }
  16. } catch (ClassNotFoundException e) {
  17. // ClassNotFoundException thrown if class not found
  18. // from the non-null parent class loader
  19. }
  20. if (c == null) {
  21. // If still not found, then invoke findClass in order
  22. // to find the class.
  23. c = findClass(name);
  24. }
  25. }
  26. if (resolve) {
  27. resolveClass(c);
  28. }
  29. return c;
  30. }

下面看一个简单的双亲委派模型代码实例验证

  1. public class ClassLoaderTest {
  2. public static void main(String[] args){
  3. //输出ClassLoaderText的类加载器名称
  4. System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());
  5. System.out.println("System类的加载器的名称:"+System.class.getClassLoader());
  6. System.out.println("List类的加载器的名称:"+List.class.getClassLoader());
  7. ClassLoader cl = ClassLoaderTest.class.getClassLoader();
  8. while(cl != null){
  9. System.out.print(cl.getClass().getName()+"->");
  10. cl = cl.getParent();
  11. }
  12. System.out.println(cl);
  13. }

输出结果为:

解释一下:

1、ClassLoaderTest类是用户定义的类,位于CLASSPATH下,由系统/应用程序类加载器加载。

2、System类与List类都属于Java核心类,由祖先类启动类加载器加载,而启动类加载器是在JVM内部通过C/C++实现的,并不是Java,自然也就不能继承ClassLoader类,自然就不能输出其名称。

3、而箭头项代表的就是类加载的流程,层级委托,从祖先类加载器开始,直到系统/应用程序类加载器处才被加载。

那么我们做个测试,把类打成jar包,拷贝入%JAVA_HOME%/jre/lib/ext目录下,再次运行ClassLoaderTest类

解释一下,因为类的Jar包放到了ExtClassLoader的加载目录下,所以在根目录找不到相应类后,在ExtClassLoader处就完成了类加载,而忽略了APPClassLoader阶段。

上一篇:JVM类加载机制详解(一)JVM类加载过程

(未完待续,下一篇:自定义的类加载器)

JVM类加载机制详解(二)类加载器与双亲委派模型的更多相关文章

  1. JVM类加载机制详解

    引言 如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 在加载阶段,虚拟机需要完成以下三件事情: 1)通过一个类的全限定名来获取定义此 ...

  2. 类文件的结构、JVM 的类加载过程、类加载机制、类加载器、双亲委派模型

    一.类文件的结构 我们都知道,各种不同平台的虚拟机,都支持 "字节码 Byte Code" 这种程序存储格式,这构成了 Java 平台无关性的基石.甚至现在平台无关性也开始演变出 ...

  3. Java虚拟机:类加载机制详解

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 大家知道,我们的Java程序被编译器编译成class文件,在class文件中描述的各种信息,最终都需要加载到虚拟机内存才能运行和使用,那么 ...

  4. 【深入理解JVM】类加载器与双亲委派模型

    原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...

  5. 【深入理解JVM】类加载器与双亲委派模型 (转)

    出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...

  6. 【深入理解JVM】:类加载器与双亲委派模型

    类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字 ...

  7. 深入理解JVM(③)虚拟机的类加载器(双亲委派模型)

    前言 先解释一下什么是类加载器,通过一个类的全限定名来获取描述该类的二进制字节流,在虚拟机中实现这个动作的代码被称为"类加载器(Class Loader)". 类与类加载器 类加载 ...

  8. JVM类加载器以及双亲委派模型

    从java开发人员的角度来看,类加载器可以分为3种: 1.启动类加载器(Bootstrap ClassLoader),负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootc ...

  9. Java自定义类加载器与双亲委派模型

    其实,双亲委派模型并不复杂.自定义类加载器也不难!随便从网上搜一下就能搜出一大把结果,然后copy一下就能用.但是,如果每次想自定义类加载器就必须搜一遍别人的文章,然后复制,这样显然不行.可是自定义类 ...

随机推荐

  1. vue.js和vue-router和vuex快速上手知识

    vue.js和vue-router和vuex快速上手知识 一直以来,认为vue相比react而言,学习成本会更低,会更简单,但最近真正接触后,发现vue的各方面都有做一些客户化的优化,有一些亮点,但也 ...

  2. ThinkPHP学习笔记(一)----初识ThinkPHP

    在做微信开发的时候原本使用来yii框架,后续觉得yii虽然功能强大使用方便,但是整个框架太大了,不适合一些轻量级的开发:这个时候发现thinkphp这个框架,框架本身很小,只有几M,但麻雀虽小,但五脏 ...

  3. 网格系统-bootStrap4常用CSS笔记

    .row 定义一行 .col 均分列数,最多一行12列.每列左右间隙各15px .col-{1到12} 定义在所有屏幕下的列宽 .col-{sm|md|lg|xl}-{1到12} 定义在指定屏幕下该列 ...

  4. PASSWORD MySQL 5.6.21-1ubuntu14.04_amd64

    /***************************************************************************** The main idea is that ...

  5. SSZipArchive解压失败的原因

    SSZipArchive 解压失败的原因是路径不对解压失败后,修正路径后再次解压就会出现解压失败的情况,代理方法也不会走也没有日志输出.解决方法就是删除手机上的软件再次运行就可以了.这个问题也就在内部 ...

  6. Binary Tree的3种非Recursive遍历

    Binary Tree Preorder Traversal Given a binary tree, return the preorder traversal of its nodes' valu ...

  7. 微信小程序开发调试技巧

    1.  查看线上小程序console a.  先打开开发小程序console b.  再打开线上小程序,此时可以查看console

  8. mongodb基本使用(一)

    1.启动.停止和重启mongodb服务 brew services start mongodb  ---启动 brew services stop mongodb --停止 brew services ...

  9. nginx中location详解

    Location block 的基本语法形式是: location [=|~|~*|^~|@] pattern { ... } [=|~|~*|^~|@] 被称作 location modifier ...

  10. [shell] bash数组(for时排序)

    for处理时会自动把顺序按A-Z排序了 [root@XM-v106 ~]# bash b.sh A -> B -> C -> D -> E -> [root@XM-v10 ...