上一篇介绍了class文件结构的含义https://blog.csdn.net/m0_37312601/article/details/82221996,这篇主要讲attribute_info表中各个属性及Java虚拟机中的一些指令。
java实例以及分析数据依然与上篇相同,这里就不重新贴了。
属性表的Code属性
方法表长度为methods_count = 2,第一个方法是编译器默认为我们添加的无参构造<init>方法。第二个方法是我们自己写的 inc()方法。属性表的Code属性存储着方法内具体代码的字节码指令,在16进制分析工具中我们将第一个构造方法表的Code属性展开如下图:
Code下包含三个指令操作(opcodes_operation): aload_0 , invokespecial... ,Return。现将对应的字节码贴出,字节码对应的指令需要查表得到,表的内容很多,贴不了。
2A:查表对应指令是aload_0,将局部变量表中第0个变量加载到操作栈,类型为引用类型reference。
B70001:0xB7,查表指令是 invokespecial,执行栈顶指向的对象的方法。0x0001指向常量池的索引,是invokespecial的参数,也是告诉虚拟机到底执行哪一个方法,对应<init>()V 构造方法。
B1: 查表为return指令,这里返回void.
结合javap 解释第二个方法inc , 在命令行中输入javap -verbose ..\TestClass。参数大小args_size=1,是默认对象this。
其他常见属性简介
- Exceptions属性:列出方法方法中可能抛出的受查异常。
- LineNumerTable属性:描述Java源码行号与字节码行号之间的对应关系。
- LocalVariableTable属性:描述栈帧中局部变量表中的变量与Java源码中定义的变量的关系,可以在javac中分别使用-g:none 或 -g:vars来取消或生成这项信息。
- SourceFile属性:记录生成这个Class文件的源码文件名称。
- ConstantValue属性:通知虚拟机自动为静态变量赋值,只有类变量才有这个属性。
- InnerClasses属性:记录内部类和宿主之间的关联。
- Signature属性:任何类,接口,初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)和参数化类型(Parameterized Types).则Signature属性会记录泛型签名信息。由于Java的泛型采用擦除法实现,为了避免类型信息被擦除后导致签名混乱,需要用这个属性记录信息。
字节码指令
java虚拟机的指令由一个字节长度,代表某种特定操作含义的数字以及紧跟其后代表参数的数值构成。
指令包含数据类型
在Java的指令集中,大多数都包含操作数据的类型信息。如 iload 在局部变量表中加载int型数据到操作数栈中,fadd 浮点型数据相加,dreturn 返回double型数据。字符对应类型{ i -int,l-long , s-short , b-byte , c-char , f-float , d-double , a-reference }。
由于操作码的长度为1个字节,所以指令总数不可能超过256个。大部分指令没有包括byte,char,solt和boolean类型。当这些类型进行运算时会使用相应的int类型运算。
加载和存储指令
用于将数据在栈帧中的局部变量表和操作数栈之间来回传输。
- 将局部变量加载到操作站:iload,lload , fload ,dload,aload。
- 将一个数值从操作数栈存储到局部变量表:istore,lstore, fstore,dstore,astore.
- 将常量加载到操作数栈:bipush, sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_m1,iconst_<i>,iconst_< l / f / 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, ishl, iushr, lshl, lshr, lushr.
- 按位或指令:ior,lor.
- 按位与指令:iand,land.
- 按位异或指令:ixor,lxor.
- 局部变量自增指令:iinc.
- 比较指令:dcmpg,dcmpl, fcmpg, fcmpl , lcmp。
- 类型转换指令:i2b(int 转 byte ), i2c , i2s , l2i , f2l , d2i , d2l和d2f。
对象创建与访问指令
- 创建类实列:new.
- 创建数组的指令:newarray, anewarray, multianewarray
- 访问类字段和示例字段:getfield,putfield, getstatic , putstatic.
操作数栈的指令
- 数栈的栈顶元素出栈:pop , pop2;
- 复制栈顶的数并压入栈顶:dup;dup2,dup_x2;
- 栈顶端数据交换:swap.