JDK类加载机制


jdk的类加载机制

类加载的过程

t7kEid.png

类加载主要分为:

加载 -> 验证 -> 准备 -> 解析 -> 初始化

  • 加载

加载的主要作用是将外部的 .class 文件,加载到 Java 的方法区内

  • 验证

验证class文件,是否符合jvm虚拟机的规范

  • 准备

类变量分配内存,并将其初始化为默认值

t7mcSH.png

类变量在准备阶段就进行了初始化,但是在方法内部定义的变量不会在准备阶段进行初始化,因此ide提示局部变量未进行

  • 解析
    解析是将符号引用替换为直接引用的过程。符号引用是一种定义,可以是任何字面上的含义,而直接引用就是直接指向目标的指针、相对偏移量。主要分为以下几个步骤:
  • 类或接口的解析
  • 类方法解析
  • 接口方法解析
  • 字段解析

几个经常发生的异常,就与这个阶段有关

  • java.lang.NoSuchFieldError 根据继承关系从下往上,找不到相关字段时的报错
  • java.lang.IllegalAccessError 字段或者方法,访问权限不具备时的错误
  • java.lang.NoSuchMethodError 找不到相关方法时的错误

解析过程保证了相互引用的完整性,把继承与组合推进到运行时

  • 初始化
    先来看一端代码
class A {
    static int a = 0;
    static {
        a = 1;
        b = 1;
        b = b + 1;
    }
    static int b = 0;

    public static void main(String[] args) {
        System.out.println(a);
        System.out.println(b);
    }
}

b = b + 1;由于b + 1中b中是变量,在静态代码块中无法引用

  • static 语句块,只能访问到定义在 static 语句块之前的变量

  • JVM 会保证在子类的初始化方法执行之前,父类的初始化方法已经执行完毕

类加载器

类加载器所做的事情就将class文件进行解析,加载到jvm的方法区;主要分为Bootstrap ClassLoaderExtention ClassLoaderApp ClassLoaderCustom ClassLoader

  • Bootstrap ClassLoader
    用于加载加载核心类库也就是 rt.jar、resources.jar、charsets.jar等

  • Extention ClassLoader
    扩展类加载器,主要用于加载lib/ext目录下的jar包和.class文件

  • App ClassLoader
    这是我们写的 Java 类的默认加载器,有时候也叫作 System ClassLoader。一般用来加载 classpath 下的其他所有 jar 包和 .class 文件,我们写的代码,会首先尝试使用这个类加载器进行加载

  • Custom ClassLoader
    自定义加载器,支持一些个性化的扩展功能。

双亲委派加载机制

双亲委派机制的意思是除了顶层的启动类加载器以外,其余的类加载器,在加载之前,都会委派给它的父加载器进行加载。这样一层层向上传递,直到祖先们都无法胜任,它才会真正的加载。

ps:这样做的目的主要是解决同一个jvm中只能加载同一个类一次

自定义加载器

  • tomcat的类加载机制

WebAppClassLoader的类加载器优先加载。等它加载不到的时候,再交给上层的 ClassLoader 进行加载。这个加载器用来隔绝不同应用的 .class 文件,比如你的两个应用,可能会依赖同一个第三方的不同版本,它们是相互没有影响的。

  • SPI机制
SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制,主要使用 java.util.ServiceLoader 类进行动态装载。

spi机制的实现是基于让线程的上下文类加载器去加载ServiceLoader,由于当前线程上下文类加载器是基于App ClassLoader的,因此并不违反双亲加载机制。


  TOC