0%

类加载

类加载过程

  • 加载
  • 连接
    • 验证
    • 准备
    • 解析
  • 初始化

1610292210392

加载

  • 通过类的全限定名,找到 Class 文件,将 Class 文件中的静态存储结构转换成方法区运行时数据结构,在内存中生活才能一个代表该类的 Class 对象,作为方法区这些数据的入口
  • 数组类型不通过类加载器创建,由 JVM 直接创建

连接

验证

验证字节码文件的安全性和完整性:包括:

  • 文件格式验证
  • 元数据验证
  • 字节码验证
  • 符号引用验证

准备

  • 为类的静态变量分配内存和设置初始化值
  • 实例变量会在对象实例化时随着对象一块分配在Java堆中。

解析

  • 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。

初始化

  • 真正执行类中定义的 Java 程序代码(字节码),初始化阶段是执行类构造器 <clinit> () 方法的过程。
  • 类初始化的情况
    • new、设置或获取类的静态资源
    • 反射调用
    • 子类初始化,先初始化父类
    • MethodHandle 和 VarHandle 可以看作是轻量级的反射调用机制
    • Java8中,接口定义了默认方法时,接口实现类发生了初始化,先初始化接口

类卸载

  • 卸载类是该类的Class对象被GC
  • 满足的条件
    • 该类的所有实例对象都已被GC,堆中不存在该类的实例对象
    • 该类没有在其他任何地方被引用
    • 该类的类加载器的实例已被GC:自定义加载器加载的类是可以被卸载掉的

类加载器

  • JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自 java.lang.ClassLoader:
    • BootstrapClassLoader( 启动类加载器) :最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib 目录下的 jar 包和类或者或被 -Xbootclasspath参数指定的路径中的所有类。
    • ExtensionClassLoader (扩展类加载器) :主要负责加载目录 %JRE_HOME%/lib/ext 目录下的jar包和类,或被 java.ext.dirs 系统变量所指定的路径下的jar包。
    • AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用 classpath 下的所有jar包和类。

双亲委派模型

  • 每一个类都有一个对应它的类加载器 。类加载的时候,会先判断这个类有没有被加载过。已经加载过的类直接返回,否则尝试加载。
  • 加载的时候,会先委派该父类加载器的 loadClass 处理,因此所有请求都会到达顶层启动类加载器。当父类加载器无法处理时,才由自己来处理。
  • 父类加载器为 null 时,由启动类加载器来处理。

双亲委派的好处

  • 保证了 Java 程序的稳定运行,可以避免类的重复加载,也保证了 Java 的核心 API 不被篡改
  • 假设每个类加载器加载自己的话,就会出现我自定义一个 java.lang.Object 类的话,运行程序的时候,就有多个 Object 类了。

打破双亲委派

自定义加载器的话,需要继承 ClassLoader 。

  • 如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。
  • 如果想打破双亲委派模型则需要重写 loadClass() 方法

----------- 本文结束啦感谢您阅读 -----------