类加载过程
2019-05-21 10:11:59 84 举报
JVM类加载
作者其他创作
大纲/内容
结束
类的<clinit>()方法执行之前,虚拟机会保证它的父类的<clinit>()方法已经执行完毕。
虚拟机将常量池中的符号引用替换为直接引用
抛出java.lang.IncompatibleClassChangeError异常
抛出java.lang.IllegalAccessError异常
当前类是否具备对该字段的访问权限?
Utf8
这个类的父类是否继承了不被允许继承的类(被final修饰的类)
(I)V
(BSCIJFDLjava/lang/Object;)I
抛出java.lang.AbstractMethodError异常
使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,而这个方法句柄所对应的类还没有进行过初始化,则需要先触发其初始化
解析成功
实例数据
否
C本身是否包含简单名称和字段描述符都与目标相匹配的字段?
解析失败
public static int value1 = 123;
是
卸载
C中是否实现了接口?
什么时候进行加载阶段虚拟机规范并没有强制约束
类A的静态变量2
#30:#31 // println:(I)V
......
将虚拟机外部的二进制字节流按照虚拟机所需的格式存储在方法区之中
Code
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
直接和间接父类的所有实例成员变量
初始化阶段是执行类构造器<clinit>()方法的过程
([Ljava/lang/String;)V
Class
准备
getstatic:读取一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)
C是接口?
加载
返回这个方法的直接引用
验证
指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量
#27:#28 // out:Ljava/io/PrintStream;
文件格式验证
Methodref
C是java.lang.Object?
#12:#13 // math:(BSCIJFDLjava/lang/Object;)I
连接
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
方法区
println
类C是否实现了接口?
字节码验证
<init>
常量池的常量中是否有不被支持的常量类型(检查常量tag标志)
类方法解析
抛出java.lang.NoSuchMethodError异常
value2设置为123
返回这个字段的直接引用
Mark Word
#2.#18 // test/Math.\"<init>\":()V
解析类方法所属的类或接口C的符号引用N
通过一个类的全限定名来获取定义此类的二进制字节流
按照继承关系从下往上递归搜索其父类
解析过程是否出现异常?
父接口中包含了简单名称和描述符都与目标相匹配的方法?
接口中包含简单名称和字段描述符都与目标相匹配的字段?
如果这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法
解析字段所属的类或接口C的符号引用N
字段表
初始化
Fieldref
value1设置为0
类A的实例方法
类A的实例字段
<clinit>()方法对于类或接口来说并不是必需的,如果一个类中没有静态初始化块,也没有对类变量的赋值操作,那么编译器可以不为这个类生成<clinit>()方法。
字段解析
按照继承关系从下往上递归搜索各个接口和它的父接口
接口C是否有父接口?
当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
类A的静态变量3
SourceFile
NameAndType
将N传递给D的类加载器去加载类C
遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化
解析
对齐填充
加载过程是否出现异常?
new:实例化一个对象
对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求
接口的<clinit>()方法执行之前,不需要先执行父接口的<clinit>()方法,只有当父接口中定义的变量使用时,父接口才会初始化。
保证方法体中的类型转换是有效的,例如可以把一个子类对象赋值给父类数据类型,这是安全的,但是把父类对象赋值给子类数据类型,甚至把对象赋值给与它毫无继承关系、完全不相关的一个数据类型,则是危险的和不合法的
验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理
确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的安全
类或接口的解析
类C本身是否包含简单名称和描述符都与目标相匹配的方法?
为静态变量分配内存并设置初始值
java/lang/System
类A的二进制字节流
使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
#29 // java/io/PrintStream
<clinit>()方法与实例构造器<init>()方法不同,它不需要显式地调用父类的<clinit>()方法,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object
#26 // java/lang/System
out
类A的方法信息
接口中包含简单名称和描述符都与目标相匹配的方法?
符号引用验证可以看作是对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验
#19 // test/Math
父类中包含了简单名称和字段描述符都与目标相匹配的字段?
静态初始化块中只能访问到定义在静态初始化块之前的类变量,定义在它之后的类变量,在前面的静态初始化块可以赋值,但是不能访问
元数据验证
生成<clinit>()方法
按照继承关系从下往上递归搜索其父接口,直到java.lang.Object类(查找范围会包括Object类)
通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的
java/io/PrintStream
当前类是否具备对该方法的访问权限?
invokestatic:调用一个类的静态方法
当前类的所有实例成员变量
虚拟机生成一个代表此数组维度和元素的数组对象
保证跳转指令不会跳转到方法体以外的字节码指令上
#22.#23 // java/lang/System.out:Ljava/io/PrintStream;
C是类?
math
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。当执行<clinit>()方法的那条线程退出<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个类加载器下,一个类型只会初始化一次。
#4.#18 // java/lang/Object.\"<init>\":()V
正式为类变量分配内存并设置类变量初始值
类A的类构造器方法<clinit>
类加载子系统
接口的实现类的<clinit>()方法执行之前,不需要先执行接口的<clinit>()方法,只有当接口中定义的变量使用时,父接口才会初始化。
在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段
符号引用验证,D是否具备对C的访问权限?
接口中不能使用静态初始化块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成<clinit>()方法。
符号引用中通过字符串描述的全限定名是否能找到对应的类
当前代码所处的类为D,要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用
保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作,例如不会出现类似这样的情况:在操作数栈放置了一个int类型的数据,使用时却按long类型来加载入本地变量表中
C是一个数组类型吗?
main
Ljava/io/PrintStream;
符号引用验证
类A的实例构造器方法<init>
类加载过程
虚拟机规范严格规定了“有且只有”5种情况必须立即对类进行初始化,而加载、验证、准备阶段需要在此之前开始
Class对象指针
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
是否以魔数0xCAFEBABE开头
LineNumberTable
#8:#9 // \"<init>\":()V
类A的静态字段
加载数组元素类型(递归解析)
使用
类A的字段信息
java/lang/Object
访问类C的字段A,如果类C中没有该字段,而类C的父接口和父类中同时有字段A,就会报编译错误:The field A is ambiguous
类A的静态变量1
一个类(下面简称A)的字节码
CONSTANT_Utf8_info型的常量中是否有不符合UTF8编码的数据
Class文件中各个部分及文件本身是否有被删除或附加的其他信息
#24.#25 // java/io/PrintStream.println:(I)V
类中的字段、方法是否与父类产生矛盾(例如覆盖了父类的final字段,或者出现不符合规则的方法重载,例如方法参数都一样,但返回值类型却不同等)
主次版本号是否在当前虚拟机处理范围之内
符号引用中的类、字段、方法的访问性(private、default、protected、public)是否可被当前类访问
Math.java
public static final int value2 = 123;
接口C本身是否包含简单名称和描述符都与目标相匹配的方法?
#2.#21 // test/Math.math:(BSCIJFDLjava/lang/Object;)I
#20 // java/lang/Object
执行<clinit>()方法
()V
访问类C的字段A,如果类C中没有该字段,对类C的各个父接口进行递归搜索,如果搜索结果指向不同的接口,就会报编译错误:The field A is ambiguous
接口方法解析
这个类是否有父类(除了java.lang.Object之外,所有的类都应当有父类)
编译器按照语句在源文件中出现的顺序收集类中所有类变量的赋值语句和静态初始化块中的语句,合并产生<clinit>()方法。
类A的类型信息
运行时常量池
类A的静态方法
putstatic:设置一个类的静态字段
test/Math
父类中包含了简单名称和描述符都与目标相匹配的方法?
0 条评论
下一页