javap 与字节码指令

xiaoxiao2021-02-28  64

javap 常用指令

-help –help -? 输出此用法消息 -version 版本信息 -v -verbose 输出附加信息 -l 输出行号和本地变量表 -public 仅显示公共类和成员 -protected 显示受保护的/公共类和成员 -package 显示程序包/受保护的/公共类 和成员 (默认) -p -private 显示所有类和成员 -c 对代码进行反汇编 -s 输出内部类型签名 -sysinfo 显示正在处理的类的 系统信息 (路径, 大小, 日期, MD5 散列) -constants 显示静态最终常量 -classpath 指定查找用户类文件的位置 -bootclasspath 覆盖引导类文件的位置

反编译 class 字节码 javap -v class,例如:

class test{ int c; int a; public static void main(String args[]){ test t = new test(); t.get(5); } test(){ super(); c = 1; a = 0; } public int get(int b){ a = b; c = a; return this.a; } }

使用javap -v 反编译字节码显示的为:

Last modified 2017-6-20; size 437 bytes MD5 checksum a42a0734a405dcc441a98574b1ac0f69 Compiled from "test.java" class test SourceFile: "test.java" minor version: 0 major version: 51 flags: ACC_SUPER Constant pool: #1 = Class #21 // test #2 = Methodref #1.#22 // test."<init>":()V #3 = Methodref #1.#23 // test.get:(I)I #4 = Methodref #7.#22 // java/lang/Object."<init>":()V #5 = Fieldref #1.#24 // test.c:I #6 = Fieldref #1.#25 // test.a:I #7 = Class #26 // java/lang/Object #8 = Utf8 c #9 = Utf8 I #10 = Utf8 a #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 Code #14 = Utf8 LineNumberTable #15 = Utf8 <init> #16 = Utf8 ()V #17 = Utf8 get #18 = Utf8 (I)I #19 = Utf8 SourceFile #20 = Utf8 test.java #21 = Utf8 test #22 = NameAndType #15:#16 // "<init>":()V #23 = NameAndType #17:#18 // get:(I)I #24 = NameAndType #8:#9 // c:I #25 = NameAndType #10:#9 // a:I #26 = Utf8 java/lang/Object { int c; flags: int a; flags: public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: new #1 // class test //创建一个test 对象,并将其放入操作数栈中 3: dup // 复制刚刚放入的引用(栈中存在两个相同的引用),放入栈中 4: invokespecial #2 // Method "<init>":()V //使用栈顶的引用调用Constant pool中#2方法即 test 的构造方法,并将引用弹出 7: astore_1 // 将this引用保存到局部变量表 索引1 的位置,然后引出栈 8: aload_1 //将 局部变量表中 1 的值,压入栈中(即对应局部变量 t) 9: iconst_5 // 将5 压入栈中 10: invokevirtual #3 // Method get:(I)I //调用使用局部变量调用其get方法,并从栈顶 中取出 方法的参数 13: pop 14: return LineNumberTable: line 5: 0 line 6: 8 line 7: 14 test(); flags: Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #4 // Method java/lang/Object."<init>":()V //构造方法调用父类的super 方法,此处调用的是Object 空的构造方法 4: aload_0 //将局部变量中的第0位(this)加载到,栈中 5: iconst_1 //将1 压入栈顶 6: putfield #5 // Field c:I //将 1 赋值给c 9: aload_0 //将局部变量中的第0位(this)加载到,栈中 10: iconst_0 //将0 压入栈顶 11: putfield #6 // Field a:I //将 0 赋值给a 14: return LineNumberTable: line 9: 0 line 10: 4 line 11: 9 line 12: 14 public int get(int); flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 //aload_0 局部变量中的第0位一般为 this,见备注 1: iload_1 //取出局部变量中 的第1位(int类型),压入栈中 2: putfield #6 // Field a:I //将 第一个局部变量赋值给 a 5: aload_0 6: aload_0 7: getfield #6 // Field a:I //获取this.a 的值,并将其压入栈中 10: putfield #5 // Field c:I //将this.a 赋值给 this.c 13: aload_0 14: getfield #6 // Field a:I //获取this.a 的值,并将其压入栈中 17: ireturn //返回this.a LineNumberTable: line 14: 0 line 15: 5 line 16: 13 } 这个class文件包含三个主要内容: - Constant Pool 包含当前类的符号 - Method 主要包含 - 方法签名和访问标识 - 字节码 - LineNumberTable 包含源码到字节码的映射,供debug使用 - LocalVariableTable 包含当前frame里的local variable,这里只有一个this变量(这里没有输出来)。

其他常见的class字节码指令:

加载和存储指令:

将一个局部变量加载到操作栈的指令包括有: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_<l>、fconst_<f>、dconst_<d> 扩充局部变量表的访问索引的指令:wide

运算指令:

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

类型转换指令:

  Java虚拟机对于宽化类型转换直接支持,并不需要指令执行,包括:

int类型到longfloat或者double类型 long类型到floatdouble类型 float类型到double类型 窄化类型转换指令包括有:i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f。但是窄化类型转换很可能会造成精度丢失。

对象创建与操作指令:

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

操作数栈管理指令:

Java虚拟机提供了一些用于直接操作操作数栈的指令,包括:pop、pop2、dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2和swap

控制转移指令:

条件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt, if_icmpgt、if_icmple、if_icmpge、if_acmpeq和if_acmpne。 复合条件分支:tableswitch、lookupswitch 无条件分支:goto、goto_w、jsr、jsr_w、ret

方法调用和返回指令:

invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),这也是Java语言中最常见的方法分派方式。 invokeinterface指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。 invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法(§2.9)、私有方法和父类方法。 invokestatic指令用于调用类方法(static方法)。 而方法返回指令则是根据返回值的类型区分的,包括有ireturn(当返回值是booleanbytecharshortint类型时使用)、lreturn、freturn、dreturn和areturn,另外还有一条return指令供声明为void的方法、实例初始化方法、类和接口的类初始化方法使用

抛出异常指令:

athrow

备注: 在非静态方法中,aload_0 表示将this 加载到操作数栈 在static 方法中,aload_0表示将方法中地第一个参数(局部变量表中的第一位),加载到操作数栈中。

参考: http://www.yangyong.me/java-class文件和虚拟机字节码指令-如何查看class文件/

http://blog.csdn.net/congwiny/article/details/18867981

http://hubingforever.blog.163.com/blog/static/17104057920117822653653

http://www.itluobo.com/2016/04/20/bytecode/

jvm 栈讲解: http://blog.csdn.net/a616413086/article/details/51272309 http://www.sohu.com/a/147500099_467808

转载请注明原文地址: https://www.6miu.com/read-64373.html

最新回复(0)