JVM类加载机制详解 类加载器与双亲委派模型

转载 2018年04月15日 10:28:51

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加载的。


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

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

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


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




双亲委派模型的意义——防止内存中出现多份相同的字节码:

比如两个类A和类B都要加载System类:

  • 如果不用委托而是自己加载自己的,那么类A就会加载一份System字节码,然后类B又会加载一份System字节码,这样内存中就出现了两份System字节码。
  • 如果使用委托机制,会递归的向父类查找,也就是首选用Bootstrap尝试加载,如果找不到再向下。这里的System就能在Bootstrap中找到然后加载,如果此时类B也要加载System,也从Bootstrap开始,此时Bootstrap发现已经加载过了System那么直接返回内存中的System即可而不需要重新加载,这样内存中就只有一份System的字节码了。

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

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

public Class<?> loadClass(String name) throws ClassNotFoundException {  
        return loadClass(name, false);  
    }  
protected synchronized Class<?> loadClass(String name, boolean resolve)  
    throws ClassNotFoundException  
    {  
    // First, check if the class has already been loaded  
    Class c = findLoadedClass(name);  
    if (c == null) {  
        try {  
        if (parent != null) {  
            c = parent.loadClass(name, false);  
        } else {  
            c = findBootstrapClassOrNull(name);  
        }  
        } catch (ClassNotFoundException e) {  
                // 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.  
            c = findClass(name);  
        }  
    }  
    if (resolve) {  
        resolveClass(c);  
    }  
    return c;  
}  


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

public class ClassLoaderTest {  
    public static void main(String[] args){  
        //输出ClassLoaderText的类加载器名称  
        System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());  
        System.out.println("System类的加载器的名称:"+System.class.getClassLoader());  
        System.out.println("List类的加载器的名称:"+List.class.getClassLoader());  
  
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();  
        while(cl != null){  
                System.out.print(cl.getClass().getName()+"->");  
                cl = cl.getParent();  
        }  
        System.out.println(cl);  
} 

输出结果为:


解释一下:

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、通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件)。而获取的方式...
  • zhangliangzi
  • zhangliangzi
  • 2016-05-07 21:19:35
  • 9235

深入理解JVM07--虚拟机类加载机制--类加载器、双亲委派模型

本文是基于周志明的《深入理解Java虚拟机》 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部实现,以便让应用程序自己决定如何去获取所...
  • oChangWen
  • oChangWen
  • 2016-05-21 23:59:02
  • 2511

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

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

jvm 类加载机制 双亲委派模型

  • 2018年04月17日 10:13
  • 359KB
  • 下载

[jvm解析系列][十]类加载器和双亲委派模型,你真的了解ClassLoader吗?

上一章我们讲到,一个类加载到内存里我们可以操作的部分只有两个,一个是加载部分一个是static{},我相信static{}不用多讲了。 接下来我们就来解析一下ClassLoader即类加载器,他就是...
  • u014629433
  • u014629433
  • 2016-06-12 12:31:54
  • 1819

JVM(三):类加载机制(类加载过程和类加载器)

使用java编译器可以把java代码编译为存储字节码的Class文件,使用其他语言的编译器一样可以把程序代码翻译成Class文件,java虚拟机不关心Class的来源是何种语言。如图所示: 在Cla...
  • boyupeng
  • boyupeng
  • 2015-08-25 15:38:32
  • 22701

JVM(四)—一道面试题搞懂JVM类加载机制

有这样一道面试题: class Singleton{ private static Singleton singleton = new Singleton(); public s...
  • noaman_wgs
  • noaman_wgs
  • 2017-07-05 19:35:20
  • 2747

222_尚学堂_高淇_java300集最全视频教程_JVM核心机制_深入类加载器_自定加密解密类加载器

深入理解JVM类加载器
  • jingjbuer
  • jingjbuer
  • 2015-06-01 14:28:22
  • 1470

jvm类加载和解析过程(总结性质)

类加载机制的定义:把class文件加载到内存,对数据进行校验解析和初始化最终形成虚拟机直接使用的java类型。具体来说:1 通过一个类的全限定名来获取定义此类的二进制字节流。2 将这个字节流所代表的静...
  • dreamytian
  • dreamytian
  • 2018-03-18 22:47:30
  • 38

JVM高级特性与实践(九):类加载器 与 双亲委派模式(自定义类加载器源码探究ClassLoader)

虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码块被称为“类加载器”...
  • ITermeng
  • ITermeng
  • 2017-07-21 17:44:38
  • 3374
收藏助手
不良信息举报
您举报文章:JVM类加载机制详解 类加载器与双亲委派模型
举报原因:
原因补充:

(最多只允许输入30个字)