乐趣区

关于类加载机制:深入解析JVM类加载机制


前言

本文带着大家初探 JVM 的类加载机制,以及双亲委派机制


一、类加载器加载的过程

类加载过程会通过:

类加载器加载的过程 包含 加载 验证 筹备 解析 初始化 应用 卸载

各阶段解析:

  • 加载:在硬盘查找并通过 IO 读取字节码文件,在加载节点生成这个类的 java.class.Class 对象
  • 验证:校验字节码文件的准确性
  • 解析:讲符号援用替换为间接援用
  • 初始化:对类的动态变量初始化为指定的值,执行动态代码块

# 二、类加载器的分类

  1. 启动类 (Bootstrap) 加载器 :加载 JVM 须要的类,会加 $JAVA_HOME/jre/lib 下的文件 底层是 C 语言实现
  2. 扩大类 (Extension) 加载器 :由 sun.misc.LauncherExtClassLoader 实现,他会加载 JAVA_HOME/jre/lib/ext 目录中的文件(或由 System.getProperty(“java.ext.dirs”) 所指定的文件)。底层是 Java 实现
  3. 利用类 (AppClassLoader) 加载器 :由 sun.misc.Launcher$AppClassLoader 实现。会加载 classpath 下的 class 及 jar 包。 底层是 java 实现
  4. 自定义加载器

三、双亲委派机制

当咱们类加载器收到一个申请的时候,首先会顺次向上查找最顶层没有父类的类类加载器(启动类加载器),顺次向下读取 class 文件,如果该类加载器曾经读取到 class 文件的时候,子节点不会再持续读取

四、双亲委派源码解析

  1. 首先,检查一下指定名称的类是否曾经加载过,如果加载过了,就不须要再加载,间接返回
  2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用 parent.loadClass(name, false);). 或者是调用 bootstrap 类加载器来加载
  3. 如果父加载器及 bootstrap 类加载器都没有找到指定的类,那么调用以后类加载器的 findClass 办法来实现类加载
//ClassLoader 的 loadClass 办法,外面实现了双亲委派机制
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {
        // 查看以后类加载器是否曾经加载了该类
        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) {
                // 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.
                long t1 = System.nanoTime();
                // 都会调用 URLClassLoader 的 findClass 办法在加载器的类门路里查找并加载该类
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {  // 不会执行
            resolveClass(c);
        }
        return c;
    }
}

五、双亲委派机制益处

为了进攻开发者为定义的类与 jdk 定义源码类产生抵触问题,保障该类在内存中的唯一性

六、如何毁坏双亲委派

  1. 自定义类加载器 重写 loadClass 办法
  2. Spi 机制绕开 loadClass 办法。以后线程设定关联类加载器

总结

本文次要介绍了类加载器加载的过程以及分类, 双亲委派机制原理以及解析等

退出移动版