JVM-双亲委派
标签:JVM

1. 类加载器 ClassLoader

ClassLoader的主要作用就是在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。

ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader通过各种方式将Class信息的二进制数据流读入系统,然后交给JVM进行连接和初始化操作。故只会影响类的加载过程。

2. ClassLoader的分类

在标准的Java程序中,虚拟机会创建三类ClassLoader,分别是:

除上面的这三个以外,还可以自定义ClassLoader。

在上面这些类加载器中,启动类加载器最为特殊,它完全由C代码实现,并且在Java中没有对象与之对应。系统的核心类就是由它加载的,是JVM的核心组件。扩展和应用类加载器都有对应的Java对象供使用。

输出ClassLoader:

package com.liuyao;

/**
 * Created By liuyao on 2018/6/3 16:21.
 */
public class PrintClassLoaderTree {
    public static void main(String[] args) {
        ClassLoader classLoader = PrintClassLoaderTree.class.getClassLoader();
        while (classLoader != null) {
            System.out.println(classLoader);
            classLoader = classLoader.getParent();
        }
    }
}

注意:无法在Java代码中访问启动类路径,当试图获得一个类的ClassLoader时,如果得到的是null,这并不意味着没有加载器为他服务,而是指加载那个类的是启动类加载器。

判断类是否被加载的时,应用类加载器会顺着双亲路径往上判断,直到启动类加载器,但是启动类加载器不会向下询问,所以这个委托路线是单向的。


这种分散的ClassLoader去加载类的好处是可以不同层次的类可以由不同的ClassLoader加载,从而进行划分,有助于系统的模块化设计。

3. 双亲委托模式的弊端

由于双亲委派是单向的,所以顶层的ClassLoader没办法访问底层的ClassLoader所加载的类。

通常情况下,启动类加载器中的类为系统核心类,包括一些重要的系统接口,而在应用类加载器中,为应用类。这样的话,应用类访问系统类没有问题,但是系统类访问应用类就会出现问题。

比如,在系统类中提供一个接口,该接口需要在应用类中得以实现,而且该接口还绑定着一个工厂方法,**接口和工厂方法在启动类加载器中,应用实例在应用类加载器中,**这样就会导致启动类加载器没办法创建由应用类加载器加载的应用类实例。比如JDBC,Xml Parser等。

4. 双全委派模式的补充

上面这两个方法分别是取得在线程中的上下文加载器设置一个线程的上下文加载器。通过这两个方法,可以把一个ClassLoader置于一个线程实例中,使该ClassLoader成为一个相对共享的实例。默认情况下,上下文加载器就是应用类加载器,这样即使在启动类加载器中也可以通过这种方式去加载应用类加载器中的类。

5. 热替换实现

注意:两个不同ClassLoader加载同一个类,在虚拟机内部,会认为这两个类是完全不同的,不能相互转化和兼容。使用这个特点可以实现热替换。

热替换基本思路:

  • 4 min read

CONTRIBUTORS


  • 4 min read