Jvm类加载机制

关于java类加载 Fate/Apocrypha

类加载的时机

一个java类的完整的生命周期会经历加载、验证、准备、解析、初始化、使用、和卸载七个阶段 其中:验证、准备、解析3个部分统称为连接

  • 主动引用: 1:遇到 new、getstatic、putstatic或invokestatic 四条指令时,这四条指令分别代表:使用new关键字实例化对象、读取或设置静态字段、调用一个类的静态方法 2:反射时,如果类没有进行过初始化 3:初始化一个类时,如果父类没有初始化,先初始化父类 4:jvm启动,执行主类,虚拟机会优先初始这个类

  • 被动引用: 有如下案例: 1:子类引用父类静态字段,不会导致子类初始化; 2:通过数组定义引用类,不会出发类的初始化; 3:常量在编译阶段存入调用类的常量池中,本质不引用定义常量的类,不会出发类的初始化

类加载

1.通过类的全额限定名获取定义此类的二进制字节流
2.将字节流代表的静态存储结构转化为方法去的运行时诗句结构
3.内存中生成一个代表这个类的java.lang.Class对象,作为方法去这个类各种数据的访问入口

数组类不通过类加载器创建,由虚拟机直接创建

验证

这个阶段是确保Class文件字节流中的信息符合当前虚拟机要求,当验证到输入字节流不符合约束,回抛出VerifyError异常

  • 文件格式验证: 是否魔数开头;主次版本号是否在当前虚拟机可处理范围内等

  • 元数据验证 是否有父类;父类是否集成了不允许被继承的类(final修饰);如果不是抽象类,是否实现了父类或接口要求实现的方法;…

  • 字节码验证 //

  • 符号引用验证 可以看作对类自身以外的信息进行匹配性校验;符号引用中的类/字段/方法的访问行是否可被当前类访问; 可能抛出类似IllegalAccessError NoSuchFieldError NoSuchMethodError等

准备

正式为类变量分配内存并设置初始值 但是注意,这里仅仅分配类变量而非实例变量,实例变量回在对象初始化时随着对象一起分配在Java堆中

public static int value = 123;这里value会被初始化0而非123,因为这时候尚未开始执行任何java方法,value赋值123是在程序便宜后,存放在类构造器`<clinit>()`方法中
:boolean会初始化为false
如果是
public static final int value = 123,则会初始化为123,ConstantValue属性(在实际的程序中,只有同时被finalstatic修饰的字段才有ConstantValue属性,且限于基本类型和String)

解析

虚拟机将常量池内符号引用替换为直接引用的过程
符号引用:

  • 符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现
    直接引用:
  • 直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
  • 相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
  • 一个能间接定位到目标的句柄 包括类或接口解析/字段解析/类方法解析/接口方法解析

初始化

是执行类构造器<clinit>()方法的过程 该方法是编译器自动收集类的所有类变量赋值动作以及静态语句块static{}中的语句合并产生的; 静态语句快中只能访问到定义在之前的变量,之后的变量只能赋值不能访问,以下会非法

staitc{
	i = 0;System.out.print(i)
}
static int i = 1;

注:由于父类的<clinit>()先执行,所以会优先于子类的变量赋值操作;线程安全; 如果在<clinit>()有耗时操作,可能也会导致多个进程阻塞;

类加载器

类与类加载器: 只有由同一个类加载器加载的才相等(equals(),isInstance(),isAssignableForm())
双亲委派模型 对于java来说,只有两种不同的类加载器,分别是启动类加载器(c++实现)和其他类加载器(java实现,继承抽象类ClassLoader) 如图 工作过程:如果类加载器收到类加载请求,会将请求委派给父类 意义:简单来说,如果没有双亲委派模型,则系统中会存在多种不同的Object类

总结

1.java类型的加载/连接/初始化实在程序运行期完成的
2.了解到类加载的时机
3.JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤 4.类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的java.lang.Class对象,用来封装类在方法区类的对象