JVM加载class文件
JVM加载class文件
类加载机制
JVM 如何加载 class 文件?Class 文件中的信息进入到虚拟机后会发生什么变化?
虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制 。
何时加载 class?
- 遇到
new
、getstatic
、putstatic
或invokestatic
这 4 条字节码指令时,如果类没有进行过初始化,则需要先触发初始化
场景:new 实例化对象时候、读取或设置一个类的静态字段的时候,以及调用一个类的静态方法的时候。
- 使用
java.lang.reflect
包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发期初始化 - 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类。
- 当使用 JDK1.7 的动态语言支持的,如果一个
java.lang.invoke.MethodHandle
实例最后的解析结果REF_getStatic
、REF_putStatic
、REF_invokeStatic
的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先触发其初始化。
类加载过程
- Loading
类的信息从文件中获取并且载入到 JVM 的内存里 - Verifying
检测读入的结构是否符合 JVM 规范的描述 - Preparing
分配一个结构用来存储类信息 - Resolving
把这个类的常量池中的所有的符号引用改变成直接引用 - Initializing
执行静态初始化程序,把静态变量初始化成指定的值
Loading 加载
在该阶段,虚拟机需要完成以下 3 件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口
获取类的二进制字节流是开发人员可控性最强的,开发人员可以定义自己的类加载器去控制字节流的获取方式(重写 loadClass() 方法)。
数组类加载:
- 如果数组的组件类型是引用类型,则递归加载这个组件类型,数组将在加载该组件类型的类加载器的类名称空间上被标识
- 如果数组的组件类型不是引用类型 (如 int[] 数组),Java 虚拟机将会把数组标记与引导类加载器关联
- 数组类的可见性与它的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为 public。
Verifying 验证
验证是连接阶段的第一步,这阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
Preparing 准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
Resolving 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
- 符号引用: 以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
- 直接引用: 可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
- 类或接口的解析
- 字段解析
- 类方法解析
- 接口方法解析
Initializing 初始化
初始化阶段是执行类构造器 <clinit>()
方法的过程
本文由作者按照 CC BY 4.0 进行授权