jvm学习笔记-chapter6 类文件结构

class类文件结构

数据及结构

  • 是一组以8位字节为基础单位的二进制流。当遇到占有8位字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储
  • 采用一种类似于C语言结构体的伪结构来存储数据
    • 无符号数:u1 u2 u4 u8
    • 表: 由多个无符号数或者其他表作为数据项构成的复合数据类型

文件格式

类型 名称 数量 说明
u4 magic 1 魔数(0xCAFEBABY)-- 确定这个class文件是否能被虚拟机接受
u2 minor_version 1 次版本号
u2 major_version 1 主版本号
u2 constant_pool_count 1 常量池容量计数值
cp_info constant_pool constant_pool_count-1 常量池中每一项常量都是一个表(14种,jdk 1.7)
字面量 – 文本字符串、声明为final的常量值
符号引用 – 类和接口的全限定名、字段的名称和描述符、方法的名称和描述符
u2 access_flags 1 一共有16个标志位可以使用(当前之定义8个)
识别一些类或者接口层次的访问信息
ACC_PUBLIC、ACC_SUPER、ACC_FINAL、ACC_INTERFACE、ACC_ABSTRACT、ACC_SYNTHETIC、ACC_ANNOTATION、ACC_ENUM
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count 包含类级变量和实例级变量(不包含局部变量)
字段表结构:access_flags(u2)、name_index(u2)、descriptor_index(u2)、attributes_count(u2)、attributes(attribute_info)
字段表集合中不会列出从超类或父接口中继承来的字段,但可能列出原本不存在的字段(譬如内部类为了保持对外部类的访问性,会自动添加指向外部类实例的字段)
u2 methods_count 1
method_info methods methods_count 方法表结构同字段表一样
方法表中的代码经过编译器编译成字节码指令后,存放在方法属性表中的“Code”属性里
如果父类方法在子类中没有被重写,方法表集合中就不会出现来自父类的方法信息,但同样有可能会出现编译器自动添加的方法,如类构造器"clinit"和实例构造器"init"
u2 attributes_count 1
attribute_info attributes attributes_count 如Code、ConstantValue、InnerClasses等等

字节码指令简介

加载和存储指令 – 将数据在栈帧中的局部变量表和操作数栈之间来回传输

* 将一个局部变量表加载到操作数栈:iload、iload_n、lload、fload、dload、aload(对象)、...
* 讲一个数值从操作数栈存储到局部变量表:i(l/f/d/a)store、i(l/f/d/a)store_n
* 将一个常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_ml、iconst_i、lconst_l、fconst_f、dconst_d
* 扩充局部变量表的访问索引指令:wide

运算指令

*  加法指令:i(l/f/d)add
*  减法指令:i(l/f/d)sub
*  乘法指令:i(l/f/d)mul
*  除法指令:i(l/f/d)div
*  求余指令:i(l/f/d)rem
*  取反指令:i(l/f/d)neg
*  位移指令:ishl、ishr、iushr、lshl、lshr、lushr
* 按位或指令:ior 、lor
* 按位与指令:iand、land
* 按位异或指令:ixor、lxor
* 局部变量自增指令:iinc
* 比较指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp
  • 补充说明:
    • java 虚拟机规范没有明确定义过整型数据溢出的具体运算结果,除非求余及除法指令时除数为0抛出异常(ArithmeticException),其余任何整数运算场景都不抛出运行时异常
    • 浮点数转为整数时,所有小数部分有效字节会被丢弃 – 向零舍入模式
    • java虚拟机在处理浮点数运算时不会抛出任何运行时异常,当一个操作溢出时,将会使用有符号的无穷大来表示,如果某个操作没有明确的数学定义的话,将会使用NaN来表示。所有使用Nan值作为操作数的算术操作,结果都返回NaN。
 		System.out.println(-9.11 > 1.11);	 		false
        System.out.println((int)-9.11);				-9
        System.out.println((int)9.11);				9
        System.out.println(Double.NaN +1);			NaN
        System.out.println(Double.NaN *2);			NaN
        System.out.println(Double.NaN /2);			NaN
        System.out.println(Double.NaN < 1); 		false

类型转换指令

对象创建与访问指令

  • 创建类实例指令: new
  • 创建数组指令: newarray、anewarray、multianewarray
  • 访问类字段(static字段,或者称为类变量)和实例字段(非static字段,或者称为实例变量)的指令:getstatic、putstatic、getfield、putfield
  • 把一个数组元素加载到操作数栈的指令:ba(ca/sa/ia/fa/da/aa)load
  • 取数组长度的指令: arraylength
  • 检查类实例类型的指令: instanceof、checkcast

操作数栈管理指令

  • 将操作数栈的栈顶一个或两个元素出站:pop、pop2
  • 复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶:dup、dup2、dup_x1、dup_x2、dup2_x1、dup2_x2
  • 将栈最顶端两个数值互换:swap

控制转移指令

  • 条件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt、…
  • 复合分支:tableswitch、lookupswitch
  • 无条件分支:goto、goto_w、jsr、jsr_w、ret

方法调用和返回指令

  • invokevirtual 用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派)
  • invokeinterface 调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找到适合的方法进行调用
  • invokespecial 用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法
  • invokestatic 用于调用类方法(static方法)
  • invokedynamic 用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法
  • 返回指令:ireturn(当返回值是boolean、byte、char、short、int类型时使用)、lreturn、freturn、dreturn和areturn、return(声明为void的方法、实例初始化方法以及类和接口的类初始化方法使用)

异常处理指令

java程序中显式抛出异常的操作(throw语句)都是由athrow指令来实现,除了用throw语句显式抛出异常情况之外,java虚拟机规范还规定了许多运行时异常会在其它java虚拟机指令检测到异常时自动抛出。
在Java虚拟机中,处理异常(catch语句)是采用异常表来完成的。

猜你喜欢

转载自blog.csdn.net/qq_28376741/article/details/86135005