前言
学习笔记
目前仅仅只是笔记
知识点
方法区在哪里?
常量池在哪里?
验证
数组类不是通过类加载器创建,它是由java虚拟机直接创建的。
验证阶段大致分为四个:文件格式验证
、元数据验证
、字节码验证
和 符号引用验证
。
文件格式验证是操作字节流的,后面三个验证阶段是基于方法区的存储结构进行的
元数据验证
:是对类的元数据信息进行语义校验,保证不存在不符合java语义规范的元数据信息。
字节码验证
:最复杂
符号引用验证
:发生在虚拟机将符号引用转为直接引用的时候,这个转换的动作发生在解析阶段。符号引用验证可以看做是对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验。
验证这个阶段是非常重要的,但是不是必要的。
如果所运行的全部代码都已经反复使用和验证过,那么在实施阶段就可以考虑使用-Xverify:none
参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都在方法区中进行分配。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程;
字段解析
1、先查找本身是否包含目标相匹配的字段;
2、假设该类实现了接口,就按照继承关系从下往上递归搜索各个接口和它的父接口,看看其是否包含目标相匹配的字段;
3、假设不是Object的话,将会按照继承的关系从下往上递归搜索其父类,看看其是否包含目标相匹配的字段;
简单总结:先找自身,再去找接口,再去找继承的类。
都找不到就扔异常:java.lang.NoSuchFieldError
异常。
找到后,接着就是权限的验证,如果没有权限就会报:java.lang.IllegalAccessError
异常。
类方法解析
先解析出类方法表的的class_index
(CONSTANT_Methodref_info
)项中的索引的方法所属的类或接口的符号引用;
1、类方法和接口方法的符号引用的常量类型是分开的
,即在不同的常量表中;如果在类方法表中发现class_index
中索引的类是个接口,那么就直接抛出java.lang.IncompatibleClassChangeError
异常。(验证该类是不是接口)
疑问:既然类方法和接口方法的符号引用是分开的,那么在class_index中怎么能发现该类是接口呢?难道是找不到就是个接口吗?
2、在该类中查找是否有简单名称和描述符都与目标相匹配的方法,有就查找结束。
3、在该类的父类中递归查找是否有简单名称和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。
4、在该类的实现的接口列表及它们的父接口之中递归查找是否有简单名称和描述符都与目标相匹配的方法。如果匹配成功,说明该类是个抽象类,查找结束,抛出java.lang.AbstractMethodError
。(说明该类方法是接口中的某个方法–并没有被实现)。
5、没找到,抛出java.lang.NoSuchMethodError
。
总结:
1、验证该类是不是接口,是的话,抛出异常。
2、在该类中进行查找
3、在该类的父类中进行查找
4、在该类实现的接口及父接口中查找,有就抛异常。
找到后,进行权限验证;
接口方法解析
接口方法也需要先解析出接口方法表的class_index
项中索引的方法所属的类或接口的符号引用,
如果解析成功,用C表示这个接口;
1、如果在接口方法表中发现class_index
中的索引C是个类而不是接口,那么就直接抛出java.lang.IncompatibleClassChangeError
异常。
2、否则,在接口C中查找是否有简单名称和描述符都与目标相匹配的方法,如果有就返回这个方法的直接引用,查找结束
3、否则,在接口C的父接口中递归,直到java.lang.Object
类(包括Object
类)为止,看是否有简单名称和描述符都与目标相匹配的方法,如果有就返回这个方法的直接引用,查找结束。
4、否则宣告查找失败。抛出java.lang.NoSuchMethodError
。
初始化
类初始化阶段是类加载过程的最后一步;
常量池
1、Class文件的常量池:Class文件之中的资源仓库;
2、运行时的常量池
方法区
方法区是堆中的一段逻辑部分,但它的名字叫Non-Heap
(非堆
)。