Class文件结构

魔数与版本

魔数,四个字节,唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件,Class文件的魔数是
0xCAFEBABE,
版本号,四个字节,前两个字节是此版本号,后两个字节是主版本号,Java的版本号从45开始,JDK1.1 之后每个大版本号在主版本上向上加1,可以推算jdk8 的主版本号是52,这让我想起来曾经某个JDK版本报错内容: unsupport version 52 ,
总览
图为在jdk1.8编译出的class文件的二进制内容

常量池

1
2
3
4
5
6
7
8
9
此处常量池并不是 Java中的常量,而是class文件中的资源仓库,是占用空间最大的数据项目之一,表数据类型项目,在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值

常量池主要存储的是 字面量 和符号引用 ,字面量是类似与Java中常量的存在,类似字符串常量,final, 符号引用是:

类和接口的全限定名(Fully Qualified Name)
字段的名称和描述符(Descriptor)
方法的名称和描述符

当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中

访问标识

紧接着的两个字节代表访问标志

操作数栈

Java虚拟机的解释执行引擎被称为”基于栈的执行引擎”,其中所指的栈就是指-操作数栈。

不同于程序计数器,Java虚拟机没有寄存器,程序计数器也无法被程序指令直接访问。Java虚拟机的指令是从操作数栈中而不是从寄存器中取得操作数的,因此它的运行方式是基于栈的而不是基于寄存器的。虽然指令也可以从其他地方取得操作数,比如从字节码流中跟随在操作码(代表指令的字节)之后的字节中或从常量池中,但是主要还是从操作数栈中获得操作数。

对于基于栈的指令集来说,最大的优势是可移植,因为它的实现不依赖硬件,不足之处在于其运行效率相对于寄存器指令集来说会慢一点;而寄存器指令集的优缺点则刚好与基于栈的指令集相反。

1
2
3
4
5
6
7
8
9
基于寄存器的:
mov ax, 1 ;把 1 放入寄存器 ax
add ax, 2 ;用 ax 的内容和 2 相加后存入 ax


基于栈的
iconst_1 //把整数 1 压入操作数栈
iconst_2 //把整数 2 压入操作数栈
iadd //栈顶的两个数相加后出栈,结果入栈

指令的操作数分两种:一种是嵌入在指令中的,通常是指令字节后面的若干个字节;另一种是存放在操作数栈中的。为了区别,我们把前者叫做嵌入式操作数,把后者叫做栈内操作数。这两者的区别是:嵌入式操作数是在编译时就已经确定的,运行时不会改变,它和指令一样存放于类文件方法表的 Code 属性中;而操作数是运行时确定的,即程序在执行过程中动态生成的

  1. 基于栈的执行引擎 是别于寄存器的 执行方式

未完。。。