Java Classloader
September 05, 2017

Java 中的类加载时机

Java 中类的生命周期包括以下 7 个阶段: 加载 验证 准备 解析 初始化 使用 卸载

初始化时机

  • 遇到new getstatic putstatic 或者 invokestatic这四种字节码指令时,对类进行初始化 new可以认定为一种特殊的静态方法

    • 读取类的被 final 修饰的静态字段,不会触发类的初始化(已在编译期把结果放入常量池) 代码实例
  • 使用反射方法对类进行反射调用的时候
  • 当初始化一个类的时候,自动初始化其父类 [代码]
  • 当虚拟机启动时,会触发执行主类(包含 main 方法的那个类)的初始化
  • 使用 JDK7 中的动态语言支持时,若是触发了java.lang.invoke.MethodHandle实例的调用,会对相应的类进行初始化

类加载的过程

  • 加载

    • 加载是类加载(Class Loading)过程的第一步
    • 在加载阶段,虚拟机需要完成以下 3 件事情
    • 通过一个类的全限定名来获取定义此类的二进制字节流
    • 将这个字节流锁代表的静态存储结构转化为方法区的运行时结构
    • 在内存中生成一个java.lang.Class对象,作为类的各种数据的访问入口
    • 加载阶段中开发人员可以通过定义自己的类加载器去控制字节流的获取方式
  • 验证

    • 连接阶段的第一步
    • 确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求
  • 准备

    • 为类变量(被 static 修饰的变量)分配内存并设置变量初始值(零值,如 int 为 0, boolean 为 false)
    • 若类变量为 ConstantValue(被 final 修饰的常量),那么在准备阶段就会初始化为常量值
  • 解析

    • 将常量池内的符号引用替换为直接引用
  • 初始化

    • 初始化是类加载(Class Loading)过程的最后一步
    • 根据程序员的代码取初始化类变量和其他资源

类加载器

作用: 通过一个类的全限定名来获取描述此类的二进制字节流任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在 Java 虚拟机中的唯一性

  • 启动类加载器(Bootstrap ClassLoader)

    • 在 HotSpot 虚拟机中由 C++语言实,是虚拟机自身的一部分
    • 负责加载<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的, 并且是虚拟机实现的类库
    • 无法被 Java 程序直接引用
  • 扩展类加载器(Extension ClassLoader)

    • sun.misc.Launcher$ExtClassLoader实现
    • 负责加载<JAVA_HOME>\lib\ext目录中的所有类库
    • 负责加载被java.ext.dirs系统变量锁指定的路径中的所有类库
    • 开发者可以直接使用扩展类加载器
  • 应用程序类加载器(Application ClassLoader)

    • 由`sun.misc.Launcher$AppClassLoader 实现
    • getSystemClassLoader()方法的返回值,也称系统类加载器
    • 负责加载用户类路径(ClassPath)上所指定的类库
    • 是程序中默认的类加载器(没有自定义自己的类加载器的时候)

双亲委派模型 (Parents Delegation Model)

  • 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器
  • 加载器之间的父子关系不会以继承(Inheritance)的关系实现,而是使用组合(Composition)关系
  • 工作过程:

    1. 如果一个类加载器收到了类加载的请求,它首先不会自己尝试加载这个类,而是先把请求委派给父类加载器取完成
    2. 只有当父类加载器反馈自己无法完成加载请求时候,子加载器才会尝试自己去加载
  • 优点: Java 类随着它的类加载器一起具备了一种带有优先级的层次关系

Question:

  • Java 类加载器都有哪些
  • JVM 如何加载字节码文件
  • 双亲委派模型是什么