在说具体的内容以前,咱们须要先理解一个概念,数据格式。java
所谓数据格式,就是数据按照约定好的格式写。数组
好比:bash
字节码的数据格式就是严格规定好,前0-3字节 是魔数,4-5字节 次版本号,6-7版本号,8-9常量池数量等。数据结构
为何0-3必定是魔术,4-7是版本号,由于这就是约定好的,你按照这个格式写,我按照这个格式读。jvm
{} 表明对象,[]表明数组,为何,由于咱们就这么约定。函数
咱们开发中可能约定, I 开头表明接口, impl结尾 表明实现类,等等。都是一种约定好的规则。ui
这里说一下个人理解spa
首先:JVM 和 class 都有属于本身的数据结构
其次:class文件在被加载到jvm内存中时候,JVM根据class文件的数据结构规则,
拆分读取class文件,而后把对应的数据放入JVM中
复制代码
加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中。code
知乎传送门cdn
class字节码的数据结构从前至后,包括 魔/版本/常量池/访问标志/类索引/父类索引/接口索引/字段表/方法表/属性表
而常量池包括:
字面量
符号引用
类加载以后,常量池的内容会进入运行时常量池,这时候里面的数据也许还保持着符号引用。
(由于解析的时机由JVM本身设定)
若是在虚拟机栈的 栈帧中,我准备调用 main() 函数,那么会经过栈帧中持有的动态链接,找到运行时常量池,
而后找到main函数的常量 好比 #2 ,若是这个常量没有被解析过,那么就经过这个常量进行解析过程,
其中包括,经过常量 找到 类名 和 nameAndType,经过 nameAndType 找到方法名和返回值。
这时候 我手里有 类名/方法名/方法返回值,下一步,我经过类名和方法名,经过JVM记录的方法列表,找到对应的方法体。
而这个方法体其实是一段内存地址,那么这时候我就把这段内存地址复制给 #2,而且给 #2设定一个已经解析的 flag。
这样就完成了 符号引用到直接引用的过程。
复制代码
虚拟机加载经历 加载--验证--准备--解析--初始化--使用--卸载,《深刻理解Java虚拟机》-周志明,书中说,加载过程当中也能够验证,我以为没问题,好比,我读取了class的前四个字段,加载进来了,而后就直接验证,看看魔数对不对,不对我就再也不加载。这个很OK。
好比魔数对了,那么我再加载四个字节,而后验证,看看版本号能不能被JVM解释,不能就报错,也很OK。