互联网科技Java BlogJava 杂谈

Java类加载机制

2019-05-27  本文已影响2人  爪哇部落格

\color{red}{码字不易,欢迎大家转载,烦请注明出处;谢谢配合}

你知道Java的类加载机制吗?字节码文件时如何被加载使用的你?今天我们从以下几个方面,一起聊聊类加载。

类加载过程

我们用一张图来梳理类的生命周期,如下所示:

类的生命周期

类加载的过程主要有三部分组成:加载、连接、初始化;其中连接可以细分为验证准备解析三个过程;那么此过程中各个阶段都分别做了什么工作呢?

加载

“加载”是“类加载”的一个阶段,在这个阶段,虚拟机主要做了以下事情:

验证

验证阶段,主要包含四个验证过程

准备

准备阶段完成的工作是给类变量(static修饰)完成内存的分配以及给类变量赋初始值;

private static int a=100;

经过准备阶段的a的初始值是0,而不是100;因为将a值赋值为100的putstatic 指令是在被编译后,存放于<clinit>构造方法之中。
基本类型的初始值如下:
![初始值](https://img.haomeiwen.com/i16709548/0ee51382785cc6bc.png?imageMogr2/aut

o-orient/strip%7CimageView2/2/w/1240)

但是也存在特殊情况,比如被final修饰的类变量将直接被赋值为指定的值;如下的在准备阶段将被赋值为100

private static final int a=100;

解析

解析阶段完成的工作是将符号引用转化为直接引用。

符号引用:以一组符号来描述引用的目标,符号可以是任何字面量,只要在使用时能无歧义的定位到目标即可。

直接引用:直接引用可以是一个直接执行目标的指针,偏移量;或者是一个能间接定位到目标对象的句柄。

初始化

初始化时类加载过程的最后一步,前面的加载过程,除了加载阶段有类加载器参与,其余阶段都有虚拟机主导和控制;到了初始化阶段才开始真正执行Java程序(字节码)。

在准备阶段已经完成过一次系统要求的赋值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其他资源。或者从另一个角度说,初始化是执行<clinit> 构造方法的过程。注意:同一个类加载器,同一个类型只会加载一次;当且仅当以下5种场景,会立马触发类的初始化:

类加载器

类和类加载器

类加载器虽然只用于实现“类加载”的动作,但其在Java程序中的作用却远远不限于类加载阶段。对于任意一个类,都需要有该类的类加载器和类本身来确定在虚拟机中的唯一性;也可以这样理解,判断两个类是否“相等”,必须建立在同一个类加载器的前提下,否则是无意义的。这里的“相等”包括,equals()isInstance()isAssignableFrom()方法返回的结果以及instanceof关键字。

双亲委派模型

从Java虚拟机的角度来看只存在两种类加载器,一种是Bootstrap ClassLoader(HotSpot由C++实现),是虚拟机的一部分;另一种是其他加载器,这些加载器由Java实现,独立于虚拟机外部,并且全部继承自抽象类ClassLoader。

从Java程序员的角度来看,类加载器划分的更加细致一些;分为Bootstrap ClassLoader (启动类加载器),Extension ClassLoader (扩展类加载器),Application ClassLoader(应用类加载器),以及User ClassLoader (用户自定义加载器)。

Bootstrap ClassLoader:虚拟机的一部分,Java程序无法直接引用;负责加载<JAVA_HOME>/lib 路径下的类库加载到虚拟机内存中。

Extension ClassLoader:由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>/lib/ext 路径下的类库加载到虚拟机内存中。

Application ClassLoader:由sun.misc.Launcher$AppClassLoader实现,负责加载。这个类加载器是CLassLoader中getSystemClassLoader()的返回值,所以也称为系统类加载器,负责加载Classpath上指定的类库,开发者也可以直接使用该加载器。

当然我们也可以根据需要自定义类加载器,各类加载器的继承层次结构如下:


加载器结构图

双亲委派的工作过程:当一个加载器收到类加载请求时,首先它不会自行加载该类,而是交由自己的父类加载器去完成,最终将类加载请求传递至Bootstrap CLassLoader;只有父类加载器反馈自己无法加载时,子类加载器才会自己尝试加载。

双亲委派模型实现

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先通过findLoadedClass判断是否已加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //父加载器不为空则由父加载器加载,否则有启动类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 父加载器加载失败
                }

                if (c == null) {
                    // 如果仍未成功加载,则调用findClass去找到该类
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读