JVM学习笔记2——虚拟机执行子系统

Posted by Ann on November 14, 2021

class类文件结构

引言

JVM最重要的特点之一就是平台无关性,做到平台无关性的基石就是class文件。任何一种语言(java、groovy、ruby)等,通过他们特殊的编译器,生成了可以在JVM中运行的class文件。

class文件是以8位字节为基础单位的二进制流。没有分隔符。

基本数据类型

class文件中只有两种基本数据类型:无符号数和表

无符号数

无符号数可以用来描述数字、索引引言、数值量、UTF-8字符串值等。 分为:u1,u2,u4,u8 表示由1,2,4,8个字节组成的无符号数。

表常以【_info】结尾,用于表示符合组合关系的i数据结构。

class文件组成

  • 魔数(前四个字节,包含文件类型、class版本号、次版本号、主版本号)
  • 常量池(大小不固定 由以下三部分组成)
    • u2无符号数,表示常量池大小
    • 字面量(字符串、声明为final的常量值等)
    • 符号引言(类&接口的全限定名、字段的名称&描述符、方法的名称&描述符)
    • 访问标志(识别该class是类还是接口,public or abstract or final等)
    • 类索引(u2无符号数,表示类的全限定名)
    • 父类索引(u2无符号数,表示父类的全限定名)
    • 接口索引(容量大小+一组u2的无符号数, 表示接口们的全限定名)
    • 字段表(除局部变量外的类变量,包含字段访问符、字段数据类型、字段名称)
    • 方法表(描述方法,类似字段表,方法描述先返回值再属性)
    • 属性表

为什么Java语言无法根据返回值的不同对方法重载?

因为Java方法表中,识别重载方法的依据是他们的特征签名不一致,特征签名指一个方法中的参数们在常量池中的引用,不包含返回值。

字节码指令

字节码由操作码+操作数组成
JVM中操作码长度为1字节,对于char、boolean等不满一字节的运算会转化成int类型字节码处理。
在class文件中执行的一些操作就是字节码指令,下面列出常见指令

  • 加载&存储指令
    • 用于堆栈中的局部变量与操作数栈交互的指令
    • 加载局部变量 -> 操作数栈:iload、fload等
    • 将数值从操作数栈存储至 -> 局部变量表:istore、fstore等
    • 加载常量 -> 操作数占 bipush
  • 运算指令
    • 在操作数栈上的特定运算
    • 加法 iadd
    • 减法 isub
    • 乘法 imul
    • 除法 idiv
    • ….
  • 类型转化指令
    • 常用于显示转化
    • 也用于上面提到的 boolean等字节码被转成int码,再转回去(字节码与数据类型无法一一对应)
  • 对象创建与访问指令
    • 创建对象 new \ newarray
    • 访问类字段 getfield \ getstatic
    • 类型检查 instanceof checkcast
  • 操作数栈管理指令
    • pop pop2(取两个栈顶元素)
    • dup 复制
    • swap
  • 控制转移指令
    • 修改PC寄存器的值(ifeq、goto等)
  • 方法调用&返回
    • invoke & ireturn
  • 异常处理指令(athrow)
  • 同步指令 (synchornized)
    • 分为方法级同步 和 方法内部一段指令同步
    • 两种同步方式通过管程实现
    • 方法级同步是隐式的,时机是方法调用和返回操作数栈时,识别标志(ACC_SYNCHRONIZED)
    • 方法内部一段指令同步通过synchronized语句来表示代码块,对应的指令为monitorreturn、monitorexit