深入理解JVM(六)——类文件结构——code

来源:互联网 发布:windows longhorn开机 编辑:程序博客网 时间:2024/05/22 14:45

Code

Java程序中方法体中的代码经过Javac编译器处理之后,最终变成字节码指令存储在Code属性内。

Code属性出现在方法表的属性集合之中,但不是所有的方法表都必须有,譬如接口或者抽象类。

Code是Class文件中最重要的一个属性,如果把一个Java程序中的信息分为代码(Code,方法体里面的Java代码)和元数据(MetaData,包括类,字段,方法定义以及其它信息)两部分

类型 名称 数量 说明 u2 attribute_name_index 1 指向constant_Utf8_info型常量的索引,常量值固定位“Code”,代表该属性的属性名称 u4 attribute_length 1 属性值的长度 u2 max_stack 1 操作数栈深度的最大值,方法执行的任何时刻不会超过这个深度,JVM根据这个值分配栈帧中操作数的深度 u2 max_locals 1 局部变量表所需的存储空间,单位Slot,4个字节,不超过32位的数据类型,每个局部变量占1个Slot,64位的用两个Slot存放。包括this,异常处理参数,方法签名参数等。Slot可以重用(超过局部变量的作用域)。 u4 code_length 1 字节码的长度 u1 code code_length 字节码指令的一系列字节流,指令的意思及参数,u1类型的长度,最多有256条指令 u2 exception_table_length 1 显示异常处理表集合,见下面的说明 exception_info exception_table exception_table_length 1 u2 attribute_count 1 1 attribute_info attributes attribute_count 1

Java虚拟机执行字节码是基于栈的体系结构

Javac编译器编译的时候会把this关键字的访问转变成一个普通方法参数的访问,只是虚拟机调用实例方法时自动传入参数而已。因此局部变量表至少会有一个指向当前对象实例的局部变量。

异常表实际上是Java代码的一部分,编译器使用异常表而不是简单的跳转命令(jsr,ret已经不用了)来实现java异常及finally处理机制。
异常处理表:有4个字段,因为较好理解所以不画表格了start_pc,end_pc,catch_pc,handler_pc
如果代码在第start_pc行到第end_pc行之间(不包括end_pc),出现了类型为catch_type或者其子类的异常(catch_type指向一个constant_class_info型常量的索引),则转到第handler_pc行继续处理。当catch_type的值为0时,代表任意异常情况都需要转向handler_pc处进行处理。

字节码指令介绍

Java虚拟机的指令由一个字节长度的,代表着某种特定操作含义的数字(操作码)以及跟随其后的零至多个代表此操作所需参数(操作数)而构成。

由于Java虚拟机采用面向操作数栈而非寄存器的架构,所以大多数的指令都不包括操作数,而只有一个操作码。

Java虚拟机的指令集中,大多数的指令都包含了其操作对应的数据类型信息。如iload指令用于从局部变量表中加载int型的数据到操作数栈中,fload则是加载float型的数据。

i代表int类型
l代表long
s代表short
b代表byte
c代表char
f代表float
d代表double
a代表reference
但是之前说过指令集的数量只有一个字节,编译器会在编译期或者运行期将byte和short类型的数据带符号扩展为相应的int类型数据,将boolean和char类型零位扩展为相应的int型数据类型。

加载和存储指令

将一个局部变量加载到操作栈:
iload,iload_< n>,
lload,lload_< n>,
fload,fload_< n>,
dload,dload_< n>,
aload,aload_< n>

将一个数值从操作数栈存储到局部变量表
istore,istore_< n>,
lstore,lstore_< n>,
fstore,fstore_< n>,
dstore.dstore_< n>,
astore,astore_< n>

将一个常量加载到操作数栈
bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_m1,
iconst_< i>,lconst_< i>,fconst_< l>,dconst_< d>

扩充局部变量表的访问索引的指令:wide

运算符指令

加法指令:iadd,ladd,fadd,dadd
减法指令:isub,lsub,fsub,dsub
乘法指令:imul,lmul,fmul,dmul
除法指令:idiv,ldiv,fdiv,ddiv
求余指令:irem,lrem,frem,drem
取反指令:ineg,leng,fneg,dneg
位移指令:ishl,ishr,iushr,lshl,lshr,lushr
按位或指令:ior,lor
按位与指令:iand,land
按位异或指令:ixor,lxor
局部变量自增指令:iinc
比较指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp

类型转化指令

虚拟机在处理窄化类型转换时,必须显示地使用转化指令
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f

对象创建与访问指令

创建类实例的指令 new
创建数组的指令 newarray anewarray multitianewarray
访问类字段(static字段,或称为类变量),实例字段的指令:
getfield putfield getstatic putstatic
把一个数组元素加载到操作数栈的指令:baload caload saload iaload laload faload daload aalaod
将一个操作数栈的值存储到数值元素的指令:bastore castore sastore iastore lastore fastore dastore aastore
取数组的长度的指令:arraylength
检查类实例类型的指令 instanceof checkcast

操作数栈转移指令

出栈一个或两个元素 pop pop2
复制栈顶一个或两个元素并将复制值重新压入栈dup dup2 dup_x1 dup2_x1 dup_x2 dup2_x2
栈顶元素互换 swap

控制转移指令

修改寄存器的值的指令
条件分支 ifeq iflt ifle ifne ifgt ifge ifnull ifnonnull …
复合条件分支 tableswitch lookupswitch
无条件分支 goto goto_w jsr jsr_w ret

方法调用和放回指令

invokevirtual 指令调用对象的实例方法
invokeinterface 调用接口的方法
invokespecial 调用一些特殊处理的实例方法(初始化,私有方法,父方法)
invokestatic 调用类方法(static)
invokedynamic 用于运行时动态解析出调用点限定符所引用的方法,并执行该方法

异常处理指令

现在Java虚拟机中用异常处理表来完成

同步指令

monitorenter
monitorexit
通常用来支持Java语言中的synchronized关键字的语义

原创粉丝点击