整个类加载过程中,除了在加载阶段用户应用程序可以自定义类加载器参与之外,其余所有的动作完全由虚拟机主导和控制。
加载
- 加载阶段,虚拟机需要完成以下三件事情:
- 通过一个类的全限定名来获取其定义的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
- 二进制字节流并不只是单纯地从Class文件中获取,比如它还可以从Jar包中获取、从网络中
获取(最典型的应用便是Applet)、由其他文件生成(JSP应用)等 - 加载阶段完成后,虚拟机外部的 二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据
链接
验证
- 验证的目的是为了确保Class文件中的字节流包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全
- 一般四个阶段的验证:文件格式的验证、元数据的验证、字节码验证、符号引用验证。
准备
- 准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。
- 该阶段有以下几点需要注意:
1.这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。- 初始值通常情况下是数据类型默认的零值(如0、0L、null、false等)
- 具体的确切值的赋值,在初始化阶段才会进行
- static final常量在编译期就将其结果放入了调用它的类的常量池中。
解析
- 解析阶段是虚拟机将常量池中的符号引用转化为直接引用的过程。
- 解析阶段可能开始于初始化之前,也可能在初始化之后开始,虚拟机会根据需要来判断,到底是在类被加载器加载时就对常量池中的符号引用进行解析(初始化之前),还是等到一个符号引用将要被使用前才去解析它(初始化之后)
- 解析动作主要针对类或接口、字段、类方法、接口方法四类符号引用进行,分别对应于常量池中的四种常量类型:
CONSTANT_Class_info、
CONSTANT_Fieldref_info、
CONSTANT_Methodref_info、
CONSTANT_InterfaceMethodref_info - static变量发生在静态解析阶段,也即是初始化之前,此时已经将字段的符号引用转化为了内存引用,也便将它与对应的类关联在了一起
初始化
- 初始化是类加载过程的最后一步,到了此阶段,才真正开始执行类中定义的Java程序代码。
- 准备阶段,类变量已经被赋过一次系统要求的初始值,而在初始化阶段,才是真正的初始化类成员变量
- 初始化阶段是执行类构造器
() 方法的过程。