美文网首页
字节码指令解读

字节码指令解读

作者: lz做过前端 | 来源:发表于2021-11-12 00:15 被阅读0次

前言

我们知道JVM被称为虚拟机,它必然要有一套程序指令集,这样CPU才能和你的程序做交互,所以Java字节码指令基本可以对应到CPU的指令。
对于JVM来说,程序的执行过程就是入栈出栈的过程。在这个过程中伴随着从局部变量表存值取值,然后压栈,然后对栈内数据做计算(出栈)。此外,可以通过程序流程控制指令(改变程序计数器),达到循环、分支、跳转的效果。如果程序需要改变对象(堆),需要用到对象操作指令。如果变量类型是兼容的,编译器还会加入转换指令(基本类型)。
按照指令的性质可以把指令分为以下几类:

  • 操作指令
    • 栈操作指令
    • 局部变量表操作指令
  • 程序流程控制指令
  • 对象操作指令
  • 算术运算指令
  • 类型转换指令
  • 调试操作指令

操作指令

栈操作指令

  • 局部变量表前入栈
    • iconst_1,表示把常数1压栈
    • 前面部分包含:i、l、f、d,分别表示int、long、fload、double(todo:对于boolean、char、byte、short是怎么处理的)。a表示references类型。
    • 当int值为-1~5时:iconst_1类似表示;用iconst_m1表示-1
    • 当int值为-128~127时:bipush 126表示
    • 当int值为-32768~32767时:sipush 32765表示
    • 当int值为-2147483648~2147483647时:ldc 2147483647表示
    • dup:压栈对象(reference类型)
  • 出栈
    • 当执行算术运算指令时,后面跟的参数从栈出

局部变量表操作指令

  • store:从栈中弹出值并存到局部变量表中
  • load:从局部变量表取出数据并加载到栈中
  • 指令前参数表示类型,指令后数字表示局部变量表槽位置:aload_1,表示从位置1取出类型为references的值并加载到栈中

程序流程控制指令

  • if_icmpge
    • if => if
    • icmp => int compare
    • ge => 前面load的两个值比较

例子:todo

对象操作指令

  • new
  • Invokestatic:顾名思义,这个指令用于调用某个类的静态方法,这是方法调用指令中最快 的一个。
  • Invokespecial :用来调用构造函数,但也可以用于调用同一个类中的 private 方法, 以及可 见的超类方法。
  • invokevirtual :如果是具体类型的目标对象,invokevirtual 用于调用公共、受保护和 package 级的私有方法。
  • invokeinterface :当通过接口引用来调用方法时,将会编译为 invokeinterface 指令。
  • invokedynamic : JDK7 新增加的指令,是实现“动态类型语言”(Dynamically Typed Language)支持而进行的升级改进,同时也是 JDK8 以后支持 lambda 表达式的实现基础。

例子:todo

算术运算指令(执行过程会按需要的参数从操作数栈弹出数据,计算完成后压栈)

  • add+
  • sub-
  • mul*
  • div/
  • rem%
  • neg-()

类型转换指令

  • 2: i2l

调试操作指令

示例及工具使用

示例1

java代码

package cc.page.jvm.study.binarycode;
public class HelloByteCode {
    public static void main(String[] args) {
        HelloByteCode obj = new HelloByteCode();
    }
}

javap查看字节码,【】内是我对代码的解释

javap -c HelloByteCode.class
public class cc.page.jvm.study.binarycode.HelloByteCode {
  public cc.page.jvm.study.binarycode.HelloByteCode();
    Code:
       0: aload_0 【从局部变量表位置0取出值,this】
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V【todo调用常量池中位置1对应的方法,()V表示返回值为void】
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class cc/page/jvm/study/binarycode/HelloByteCode【new一个对象,参数指向常量池#2也就是HelloByteCode类地址】
       3: dup【将上面的对象压栈】
       4: invokespecial #3                  // Method "<init>":()V【调用对象方法init,返回值为void】
       7: astore_1【从栈中弹出弹出值并保存到局部变量表位置1处】
       8: return
}
注:字节码前面的数字代表在真正的字节码文件中的偏移位置,一个位置一个字节

打印局部变量表和常量池等信息
注意,常量池#1指向的应该就是常量池的标识符,值应该是存在运行时常量池中,所以应该还要去常量池

javap -c -verbose HelloByteCode.class
Classfile /Users/xuqingbin/Documents/myself/架构师进阶/workspace/jvm/target/classes/cc/page/jvm/study/binarycode/HelloByteCode.class
  Last modified 2021-11-11; size 473 bytes
  MD5 checksum 90b484786b1f7f825804e420dd281f31
  Compiled from "HelloByteCode.java"
public class cc.page.jvm.study.binarycode.HelloByteCode
  minor version: 0
  major version: 52【Java8】
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#19         // java/lang/Object."<init>":()V
   #2 = Class              #20            // cc/page/jvm/study/binarycode/HelloByteCode
   #3 = Methodref          #2.#19         // cc/page/jvm/study/binarycode/HelloByteCode."<init>":()V
   #4 = Class              #21            // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               LocalVariableTable
  #10 = Utf8               this
  #11 = Utf8               Lcc/page/jvm/study/binarycode/HelloByteCode;
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = Utf8               args
  #15 = Utf8               [Ljava/lang/String;
  #16 = Utf8               obj
  #17 = Utf8               SourceFile
  #18 = Utf8               HelloByteCode.java
  #19 = NameAndType        #5:#6          // "<init>":()V
  #20 = Utf8               cc/page/jvm/study/binarycode/HelloByteCode
  #21 = Utf8               java/lang/Object
{
  public cc.page.jvm.study.binarycode.HelloByteCode();
    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 3: 0
      LocalVariableTable:【局部变量表】
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcc/page/jvm/study/binarycode/HelloByteCode;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class cc/page/jvm/study/binarycode/HelloByteCode
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
            8       1     1   obj   Lcc/page/jvm/study/binarycode/HelloByteCode;
}
SourceFile: "HelloByteCode.java"

示例2

参考资料

相关文章

  • 字节码指令解读

    前言 我们知道JVM被称为虚拟机,它必然要有一套程序指令集,这样CPU才能和你的程序做交互,所以Java字节码指令...

  • 浅谈 JVM 3:指令集及其执行

    上文我们聊了如何参考 JVM 规范 解读 Java 字节码。但对于字节码方法字段 Code 中的 JVM 指令 执...

  • Java 字节码指令

    字节码指令链接

  • JVM-06

    switch-case的字节码指令: Java代码如下: 字节码指令如下: 结论是:switch-case 语句 ...

  • Java虚拟机-字节码指令

    1 字节码指令 Java字节码指令的执行离不开操作数栈,局部变量表,和常量池。 2 常量池 对于字节码指定来说,常...

  • Java虚拟机知识点【字节码】

    字节码指令   Java虚拟机的字节码指令由一个字节长度,代表着某种特定操作含义的操作码以及跟随其后的零至多个代表...

  • 字节码指令

    介绍 jvm字节码指令就是由一组带有特定操作含义的数字并且后面跟上操作参数组成。 加载和存储指令 将数据从栈桢中的...

  • 字节码指令

    指令由一个字节码长度的、操作码(代表某种特定操作含义的数字)以及操作数(紧跟操作码后的0到多个参数)构成。 Jav...

  • 字节码指令

    描述 本文摘自深入理解Java虚拟机中关于字节码的介绍,部分指令参考,oracle字节码指令集。 数据类型:byt...

  • Java ByteCode

    什么是Java字节码指令?简而言之,Java字节码指令就是Java虚拟机能够听得懂、可执行的指令,可以说是Jvm层...

网友评论

      本文标题:字节码指令解读

      本文链接:https://www.haomeiwen.com/subject/jivezltx.html