::我的理解

笔记

类加载子系统,主要的作用就是将class文件,加载到内存中。解析、初始化数据,相应放到方法区,常量区。最终生成虚拟机可以使用的JVA类型。方便后续配合程序计数器执行。这里的描述,简化了一些细节,比如校验,初始化等步骤。

准确概述

Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、准备、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的加载机制。Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这里就是我们经常能见到的Class类

类的生命周期

类的加载过程


详细版本:

  • 加载

    1. 将class文件加载到内存
    2. 将静态数据结构转化成方法区中运行时的数据结构
    3. 在堆中生成一个代表这个类的 java.lang.Class对象作为数据访问的入口
  • 连接阶段

    • 校验

      确保加载的类符合 JVM 规范和安全,保证被校验类的方法在运行时不会做出危害虚拟机的事件,其实就是一个安全检查

    • 准备

      为static变量在方法区中分配内存空间,设置变量的初始值,例如 static int a = 3 (注意:准备阶段只设置类中的静态变量(方法区中),不包括实例变量(堆内存中),实例变量是对象初始化时赋值的)

    • 解析

      虚拟机将常量池内的符号引用替换为直接引用的过程(符号引用比如我现在import java.util.ArrayList这就算符号引用,直接引用就是指针或者对象地址,注意引用对象一定是在内存进行)

  • 初始化

    初始化其实就是执行类构造器方法的<clinit>()的过程,而且要保证执行前父类的<clinit>()方法执行完毕。这个方法由编译器收集,顺序执行所有类变量(static修饰的成员变量)显式初始化和静态代码块中语句。此时准备阶段时的那个 static int a 由默认初始化的0变成了显式初始化的3。 由于执行顺序缘故,初始化阶段类变量如果在静态代码块中又进行了更改,会覆盖类变量的显式初始化,最终值会为静态代码块中的赋值。

类加载时机

JVM 会在程序第一次主动引用类的时候,加载该类,被动引用时并不会引发类加载的操作。也就是说,JVM 并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。那么什么是主动引用,什么是被动引用呢?

主动使用,又分为七种情况:

  • 创建类的实例
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法I
  • 反射(比如:Class.forName(“com.atguigu.Test”))
  • 初始化一个类的子类
  • Java虚拟机启动时被标明为启动类的类
  • JDK7开始提供的动态语言支持:
  • java.lang.invoke.MethodHandle实例的解析结果REF getStatic、REF putStatic、REF invokeStatic句柄对应的类没有初始化,则初始化

除了以上七种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化。

参考资料

JavaGuide · GitHub