跳至主要內容

JVM类加载机制

pptg大约 4 分钟

1. 类加载过程

1.1 类的生命周期

类从被加载到虚拟机内存中开始到卸载出内存为止,它的整个生命周期可以简单概括为 7 个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,验证、准备和解析这三个阶段可以统称为连接(Linking)

类生命周期
类生命周期

1.2 类的加载过程

类生命周期中的加载、验证、准备、解析和初始化这五步即为类的加载过程

1.2.1 加载

  • 通过全类名获取定义此类的二进制字节流
  • 字节流所代表的静态存储结构转换为方法区的运行时数据结构
  • 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口

1.2.2 验证

确保Class 文件的字节流中包含的信息符合《Java 虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机的安全

1.2.3 准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段

1.2.4 解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

1.2.5 初始化

初始化阶段是执行初始化方法 <clinit> ()方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)

1.3 类的卸载

类的卸载需要满足三个要求:

  • 对象被GC:该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。
  • 无引用:该类没有在其他任何地方被引用
  • 类加载器被GC:该类的类加载器的实例已被 GC

2. 类加载器

2.1 介绍

每个Java类都有一个引用指向加载自身的ClassLoader数组类由JVM直接生成)。类加载器的主要作用是加载Java类的字节码(.class文件)到JVM中

2.2 JVM类加载器

JVM内置了三个重要的类加载器:

  • BootstrapClassLoader(启动类加载器):最顶层的加载类,由 C++实现,通常表示为 null,并且没有父级,主要用来加载 JDK 内部的核心类库%JAVA_HOME%/lib目录下的 rt.jar、resources.jar、charsets.jar等 jar 包和类)以及被 -Xbootclasspath参数指定的路径下的所有类
  • ExtensionClassLoader(扩展类加载器):主要负责加载加载扩展类%JRE_HOME%/lib/ext 目录下的 jar 包和类以及被 java.ext.dirs 系统变量所指定的路径下的所有类
  • AppClassLoader(应用程序类加载器):面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类

除此之外,也可以自定义类加载器继承自ClassLoader。比如对class进行加密,并用类加载器进行解密。

ClassLoader类中有两个关键方法:

  • protected Class loadClass(String name, boolean resolve):加载二进制名称的类,实现了双亲委派机制
  • protected Class findClass(String name):根据类的二进制名称来查找类,默认为空

如果不想打破双亲委派模型,只需重写findClass即可。这样会自动通过父类加载,如果未被加载的话则会调用该方法加载

2.3 双亲委派模型

  1. 双亲委派模型具有以下机制:
  • ClassLoader 类使用委托模型来搜索类和资源
  • 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器
  • ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器
  1. 双亲委派的执行过程如下:
  • 首先判断是否被加载过,若未被加载过会尝试加载
  • 加载时调用父类的loadClass()来加载,最终会到达BootstrapClassLoader
  • 若父类无法加载,会返回并调用子类的findClass来加载
  • 如果都无法加载则抛出异常
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        //首先,检查该类是否已经加载过
        Class c = findLoadedClass(name);
        if (c == null) {
            //如果 c 为 null,则说明该类没有被加载过
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //当父类的加载器不为空,则通过父类的loadClass来加载该类
                    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) {
            //对类进行link操作
            resolveClass(c);
        }
        return c;
    }
}