jvm-字节码文件

背景

什么是字节码文件?

源代码经过编译器编译之后生成的一种二进制文件。它的内容是JVM指令,不像C、C++一样由编译器直接生成机器码。

什么是字节码指令?

Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。
字节码指令主要是操作虚拟机栈的栈帧中的数据。

怎么查看字节码文件?
  1. idea中安装Jclasslib插件,选择顶部菜单栏的 View->Show Bytecode With Jclasslib 选项,即可查看当前类的字节码文件。
  1. 下载Jclasslib客户端,选择它用来打开class文件。

  2. 我们可以用javap(jdk自带的反解析工具)来查看一个class文件的字节码指令文件:

javap  -verbose  Main.class

eg.

public com.example.demo.Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/example/demo/Main;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: iconst_1
         1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         4: astore_1
         5: sipush        129
         8: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        11: astore_2
        12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        15: new           #4                  // class java/lang/StringBuilder
        18: dup
        19: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        22: aload_1
        23: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
        26: ldc           #7                  // String ,
        28: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        31: aload_2
        32: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
        35: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        38: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        41: return
      LineNumberTable:
        line 9: 0
        line 10: 5
        line 11: 12
        line 12: 41
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      42     0  args   [Ljava/lang/String;
            5      37     1     a   Ljava/lang/Integer;
           12      30     2     b   Ljava/lang/Integer;

字节码指令分析

1. 加载和存储指令

用于将数据在栈帧中的局部变量表操作数栈之间来回传输

指令含义示例
load将一个局部变量加载到操作数栈iload 1:将int型本地变量1加载到操作数栈顶中
store将一个数值从操作数栈存储到局部变量表astore_0( 0-3 ):将操作数栈顶引用型(reference)数值存入到第一个局部变量中
push将一个整形数字(长度比较小)送到到栈顶bipush:将单字节的常量值(-128至127)推送至栈顶;sipush:将一个短整型常量值(-32768至32767)推送至栈顶
ldc将数值常量或String常量值从常量池中推送至栈顶ldc: 将int, float或String型常量值从常量池中推送至栈顶;ldc_w: 将int, float或String型常量值从常量池中推送至栈顶(宽索引);ldc2_w: 将long或double型常量值从常量池中推送至栈顶(宽索引)
const把简单的数值类型( iconst_(0 - 5) )送到栈顶,不带参数aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>;iconst_m1:将int型的-1推送至操作数栈顶;fconst_1:将float型的1推送至操作数栈顶
wide扩充局部变量表的访问索引
2. 运算指令

用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作数栈顶

指令含义示例
add加法支持 i, l, f, d;iadd:将栈顶两int型数值相加并将结果压入栈顶
sub减法支持 i, l, f, d
mul乘法支持 i, l, f, d
div除法支持 i, l, f, d
rem求余支持 i, l, f, d
neg取反支持 i, l, f, d
sh位移支持 i, l;ishl:将int型数值(有符号)左移位指定位数并将结果压入栈顶;iushr:将int型数值(无符号)右移位指定位数并将结果压入栈顶
or按位或支持 i, l;ior:将栈顶两int型数值作“按位或”并将结果压入栈顶
and按位与支持 i, l;
xor按位异或支持 i, l;
iinc局部变量自增
cmp比较支持 d, f, l;dcmpg、dcmpl、fcmpg、fcmpl、lcmp
3. 类型转换指令

将两种不同的数值类型进行相互转换。
java虚拟机支持从小范围到大范围(如int到long/float/double)的直接转换,无需显式的转换指令,当然你也可以显式写出来;而从大范围到小范围的转换必须要显式的使用转换指令来完成。

指令含义示例
i2l、f2b、l2f、l2d、f2d宽化类型转换
i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f窄化类型转换
4. 对象创建与访问指令

创建对象和创建数组,以及访问对象和访问数组的指令

指令含义示例
new新建类
newarray新建数组
getfield、putfield、getstatic、putstatic访问类变量,静态变量
aload将一个数组元素加载到操作数栈支持 b, c, i, l, f, d;iaload:将int型数组的指定索引的元素加载到操作数栈顶中
astore将一个操作数栈顶的值存储到数组元素指定索引中支持 b, c, i, l, f, d;dastore:将操作数栈顶中double型数值存入到指定数组的指定索引位置
arraylength取数组长度
instanceof、checkcast检查实例类型
5. 操作数栈管理指令

直接操作操作数栈的指令

指令含义示例
pop、pop2将操作数栈顶1或2个元素出栈
dup、dup2、dup_x1、dup_x2、dup2_x1、dup2_x2复制1或2份操作数栈顶1或2个元素,重新压入栈顶
swap将操作数栈最顶端两个元素互换
6. 控制转移指令

可以让java虚拟机从 指定位置 的指令的下一条指令继续执行程序,可以认为控制转移指令就是在有条件或无条件地修改PC寄存器的值。

指令含义示例
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无条件分支
7. 方法调用和返回指令
指令含义示例
invokevirtual调用对象实例,根据对象的实际类型进行分派(虚方法分派)
invokeinterface调用接口方法,它对在运行时搜索一个实现了这个接口方法的对象,找出合适的方法进行调用
invokespecial调用一些需要特殊处理的实例方法,如实例初始化方法、私有方法、父类方法
invokestatic调用类方法(static方法)
invokedynamic执行在运行时动态解析出调用点限定符所引用的方法,invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的
8. 异常处理指令
指令含义示例
athrow程序中显式抛出异常(throw语句)
9. 同步指令

java虚拟机支持方法级的同步和方法内部代码块的同步,这两种同步结构都是使用管程(Mentor)来支持的。

方法级的同步是隐式的,即无须通过字节码指令来控制。它实现在方法调用和返回操作之中。虚拟机可以从方法常量池的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否声明为同步方法。当方法调用时,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程。在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那么这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放。

方法内部代码块的同步需要显式声明指令,来支持synchronized关键字语义

指令含义示例
monitorenter、monitorexit方法内部代码块的同步,调用过的每个monitorenter指令必须要执行其对应的monitorexit指令

相关:https://blog.csdn.net/YABAJ/article/details/89240707

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值