jvm类加载机制

自我记录  自我记录。。。

摘自:深入理解JVM虚拟机

加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载

有且只有五种状况需要立即进行类的初始化操作:

1、遇到new、getstatic、putstatuc、invokestatic这四条指令

2、java.lang.reflect中的方法进行反射调用

3、对某一个类进行初始化,但是其父类还没有初始化,则先触发父类的初始化

4、虚拟机启动时,需要执行一个主类

5、java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、 REF_putStatic、 REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

这几种情形下,称为对类的主动引用,除此之外的引用类的方法均称为被动引用


常量传播优化!!

public static final String a = "abc";

这类的常量在编译阶段已经通过常量传播优化,被优化到了NotInitialization类的常量池中。

注意:当一个类初始化的时候,要求它的父类都已初始化,而当接口初始化时,并不要求它的父接口初始化!


加载:

1、通过一类的全限定名找到此类的二进制文件

2、将这个二进制流文件中的静态存储结构转换成方法区中的运行时数据结构

3、在内存中生成代表这个类的class对象,作为访问入口

一个非数组类的加载阶段是可控性最强的,因为可以自己指定类加载器进行加载,注意是非数组类,数组类本身不由类加载器加载,而是直接由虚拟机加载

验证:

1、文件格式验证

确保输入的字节流能正确的解析并且存放到方法区中

2、元数据验证

进行语义分析,保证符合java规范

3、字节码验证

通过数据流和控制流分析,确保程序语义是合法的,符合逻辑的。再对元数据信息中的数据类型进行校验后,这个阶段对类的方法体进行校验分析。

4、符号引用验证

发生在符号引用转为直接引用的时候,该阶段可当做是对类自身之外(常量池中的各种符号引用)的信息进行校验。

符号引用验证是确保解析动作能够正常运行 

准备:

这个阶段正式为变量分配内存,并设置变量初始值,分配的内存位于方法区

此时进行分配的变量仅仅是类变量(static修饰),不包括实例变量。

public static int a = 123;

a变量再准备阶段过后的值为0,此时尚未开始执行任何java方法,赋值的操作是putstatic指令,该指令存放于类构造器<clinit>()方法中,因此赋值操作是在初始化阶段中进行。

还有一种特殊情况,比如:

public static final int b = 123;

此时类字段的字段属性表中存在ConstantValue属性,那么在准备阶段b就会被赋值为123。

解析:

解析阶段是虚拟机将常量池中的符号引用替换成直接引用的过程

符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。

直接引用:直接引用可以是直接指向目标的指针、 相对偏移量或是一个能间接定位到目标的句柄。

如果有了直接引用,那引用的目标必定已经在内存中存在。

对同一个符号进行多次引用的情况:

除了invokedynamic命令,虚拟机会可以对第一次解析的结果进行缓存,避免重复操作。一次成功,次次成功,一次失败,次次报错;

初始化:

这个阶段才真正开始执行类中定义的java程序代码(字节码),初始化阶段即时执行<clinit>()方法的过程。

<clinit>()方法是有编译器自动收集的类中所有的类变量赋值动作静态语句块中(static{})的语句合并产生的,执行顺序按照书写顺序来!

<clinit>()方法不需要显示的调用父类构造器,虚拟机会确保,在子类的<clinit>()方法执行前,先执行父类的<clinit>()方法,因此java.lang.Object类的<clinit>()方法肯定是第一个执行的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值