理解JVM的类加载过程

发布于:2022-12-17 ⋅ 阅读:(445) ⋅ 点赞:(0)

Ⅰ、加载

完成三件事:

  1. 通过此类的全类名 来获取定义此类的二进制字节流

  2. 将该字节流所代表的静态数据存储结构 转化为方法区的运行时数据结构

  3. 在内存中生成一个代表这个类的 java.lang.Class对象,作为方法区这个类各种数据的访问入口。

加载阶段可以使用虚拟机内置的引导类加载器完成,也可以由用户自定义的类加载器去完成。但是对于数组类情况有所不同,数组类本身不由类加载器创建,是由java虚拟机直接在内存中动态构造出来的。但是数组的元素类型还是需要靠类加载器来完成加载。如果数组组件类型是引用类型,则递归加载过程进行加载,如果不是,则java虚拟机将会把数组标记为与引导类加载器相关联。

Ⅱ、验证(Verify)工作量占了较大比重

目的:

确保Class文件中的字节流符合《java虚拟机》的全部约束和规范,保证这些信息被当作代码运行后不会损害到虚拟机的安全。

包含四种验证:

  1. 文件格式验证 (其中一点:是否以魔数0xCAFEBABE开头) 通过该验证字节流才能进入java虚拟机内存的方法区进行存储,所以后面三种验证会基于java虚拟机内存存储结构进行,而不会在读取、操作字节流了

  2. 元数据验证

  3. 字节码验证

  4. 符号引用验证

Ⅲ、准备(prepare)

为类变量分配内存并设置默认初始值

两点注意:

  1. 不包含用final修饰的static,这个在编译时即分配,准备阶段即会显式初始化 比如public static final int value = 123 在准备阶段虚拟机就会直接根据ConstantValue的设置将value赋值为123 而public static int value = 123 在准备阶段后初始值应该为0,而不是123

  2. 不会为实例变量分配初始化,类变量分配在方法区,而实例变量会随对象一起分配到java堆中

Ⅳ解析(Resolve)

将常量池内的符号引用转化为直接引用的过程

解析动作主要针对:类,接口,字段,类方法,接口方法,方法类型等

Ⅴ、初始化

该阶段,java虚拟机真正开始执行类中编写的java程序代码,将主导权交给应用程序

直接的表达:初始化阶段就是执行类构造器<clinit>()方法的过程

关于<clinit>()

  1. 该方法并不是程序员直接在java程序中编写的代码,时javac编译器中自动生成的产物

  2. 该方法由编译器 自动收集 类中类变量的赋值动作 和 静态代码块的语句合并产生的

  3. 该方法与类的构造函数不同(虚拟机视角应该为<init>()方法),无需显示调用父类构造器,虚拟机保证了子类该方法执行前,父类已经执行完毕,所以java虚拟机中第一个被执行的<clinit>()方法的类型一定是java.lang.Object

  4. 该底层也证明了,父类的静态代码块会优先于子类类变量的赋值操作。

  5. 该方法对类或者接口不是必须的,可以不生成

  6. java虚拟机必须保证一个类的<clinit>()方法在多线程的情况下被正确地同步加锁。


网站公告

今日签到

点亮在社区的每一天
去签到