类加载机制

Java类加载机制

类加载就是将类的.class文件的二进制数据读入到内存,并放在运行时数据区的方法区,并在堆内存中创建Class对象,用来封装方法区内二进制文件流的数据结构。
类加载的最终产品就是在堆内存中的Class对象

类生命周期

1. 加载:找class文件在哪

  1. 通过一个类的全限定名来获取二进制字节流
  2. 将字节流转化为方法区的运行时数据结构
  3. 在堆中创建一个表示该类的java.lang.Class对象,作为访问方法区中运行时数据的入口

2. 验证:验证class文件格式规范、语义分析、引用验证、字节码验证

  1. 文件格式验证,检验字节流是否符合class文件格式规范
  2. 元数据验证,堆字节码描述的信息进行语义分析
  3. 字节码验证,分析控制流和数据流语序语义是否正确
  4. 符号引用验证,确保动作能正确执行

3. 准备:分配内存、设置类static修饰的变量初始值
4. 解析:类、接口、字段、类方法等解析
5. 初始化:为静态变量赋值;执行静态代码块
6. 使用:创建实例对象
7. 卸载:从JVM方法区中卸载

2.3.4合称连接

1.加载

  1. 通过一个类的全限定名来获取二进制字节流
  2. 将外部二进制流结构转换为JVM所需的格式存储在方法区
  3. 在java堆中生成一个代表这个类的Class对象,作为访问方法区中二进制流的入口

2.验证

  1. 文件格式验证,验证字节流是否符合Class文件格式规范
  2. 元数据验证,堆字节码描述的信息进行语义分析,保障信息符合Java语言规范
  3. 字节码验证,通过数据流和控制流分析语义是否合法、合逻辑
  4. 符号引用验证,确保解析动作顺利执行

3.准备

  1. 进行内存分配,静态变量设置初始值(默认的初始值,用户指定的赋值在初始化阶段,但是如果字段被final static同时修饰,那么也会在准备阶段进行用户指定的赋值)

4.解析

  1. 把类中的符号引用,转换为直接引用
  2. 符号引用:类,接口,字段,类方法,接口方法,方法类型,方法句柄和调用限定符, 它们保存在常量池
  3. 直接引用:直接指向目标的指针

5.初始化

为静态变量赋予正确的值

  1. 如果这个类还没有被加载和连接,则要先加载并连接该类
  2. 假如该类的直接父亲还没有被初始化,则先初始化直接父类
  3. 假如类中有初始化语句,则依次执行这些初始化语句

类初始化的触发时机

  1. 当虚拟机启动时,初始化用户指定的类(main)
  2. 当遇到用于新建目标类实例的指令时(new), 初始化目标类
  3. 当遇到静态方法的指令时,初始化静态方法所在类
  4. 当遇到访问静态属性的指令时,初始化静态属性所在的类
  5. 子类的初始化会触发父类的初始化
  6. 如果一个接口定义了default方法, 那么直接或间接的实现该接口类的初始化方法, 会触发该接口的初始化
  7. 使用反射API对某个类进行反射调用时会触发初始化这个类
  8. 当调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类

类不会初始化(可能加载)

  1. 通过子类引用父类的静态字段只会触发父类的初始化,而不会触发子类的初始化
  2. 定义对象数组, 不会触发该类的初始化
  3. 常量在编译期间会存入调用类的常量池,本质上没有直接引用定义常量的类,不会触发定义常量类的初始化
  4. 通过类名获取Class对象, 不会触发类的初始化
  5. 通过Class.forName加载指定类,如果指定参数initialize为false时,不会触发初始化
  6. 通过ClassLoader默认的loadClass方法,也不会触发初始化动作

类加载器

类加载器负责装入类,搜索各个位置的类资源.
一个Java程序运行,至少有三个类加载器实例,负责不同类的加载

  1. Bootstrap Loader 核心类库加载器,由C语言实现,加载JDK核心类库
  2. Extension Class Loader 拓展类库加载器,加载JDK拓展包
  3. application class loader 用户应用程序加载器,加载classpath路径下的包

类不会重复加载

类的唯一性:同一个类加载器,类名一样,代表同一个类.

双亲委托机制

某一个类加载器想要去加载一个类,并不会立刻去加载,而是委托给父亲,如果父亲上面还有父亲,则继续向上委托,最终到达核心类库加载器,如果核心类库无法记载,再向下一个个委托.
即,有父类交给父类加载,没父类了开始加载,无法加载向下委托,全都不能加载则失败

通过双亲委派机制

  1. 建立类的层级关系可以避免类的重复加载,当父亲已经加载该类时,就没必要再加载一次了.
  2. 优先加载核心类库, 这样防止用户恶意的同名类,因为永远是核心的类先被加载.