深入理解JVM虚拟机第二十二篇:详解JVM当中与操作数栈相关的字节码指令

大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。

孙哥链接:孙哥个人主页
作者简介:一个颜值99分,只比孙哥差一点的程序员
本专栏简介:话不多说,让我们一起干翻JVM

本文章简介:话不多说,让我们讲清楚JVM当中与操作数栈相关的字节码指令

文章目录

[一: 操作数栈字节码指令](#一: 操作数栈字节码指令)

1:编写源码

2:javap解释整理字节码

3:通过jclasslib查看字节码指令

二:字节码分析

1:最全字节码指令分析

2:面试题


一: 操作数栈字节码指令

1:编写源码

java 复制代码
public class OperandStackTest {
    public void testAndOperation(){
        byte i = 15;
        int j = 8;
        int k = i+j;
    }
}

2:javap解释整理字节码

想要查看字节码文件呢,我们有两种方式,第一种就是直接进行javap,第二种就是使用jclasslib进行查看,我们先使用第一种。

java 复制代码
PS D:\code\study\hadoop\shit\target\classes> javap -verbose .\OperandStackTest.class
Classfile /D:/code/study/hadoop/shit/target/classes/OperandStackTest.class
  Last modified 2023年11月9日; size 421 bytes
  SHA-256 checksum 487149a1edc4d19af0b1fe2369086c27c8765ff5e011491d1028d4f6cf1d9746
  Compiled from "OperandStackTest.java"
public class OperandStackTest
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2                          // OperandStackTest
  super_class: #3                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #3.#19         // java/lang/Object."<init>":()V
   #2 = Class              #20            // OperandStackTest
   #3 = Class              #21            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               LOperandStackTest;
  #11 = Utf8               testAndOperation
  #12 = Utf8               i
  #13 = Utf8               B
  #14 = Utf8               j
  #15 = Utf8               I
  #16 = Utf8               k
  #17 = Utf8               SourceFile
  #18 = Utf8               OperandStackTest.java
  #19 = NameAndType        #4:#5          // "<init>":()V
  #20 = Utf8               OperandStackTest
  #21 = Utf8               java/lang/Object
{
  public OperandStackTest();
    descriptor: ()V
    flags: (0x0001) 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 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LOperandStackTest;

  public void testAndOperation();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: bipush        15
         2: istore_1
         3: bipush        8
         5: istore_2
         6: iload_1
         7: iload_2
         8: iadd
         9: istore_3
        10: return
      LineNumberTable:
        line 3: 0
        line 4: 3
        line 5: 6
        line 6: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   LOperandStackTest;
            3       8     1     i   B
            6       5     2     j   I
           10       1     3     k   I
}
SourceFile: "OperandStackTest.java"

3:通过jclasslib查看字节码指令

首先进行recompile Java文件为字节码文件,然后我们在idea的view下找到这个:

show ByteCode with JclassLib:

最终显示结果如下:

二:字节码分析

1:最全字节码指令分析

java 复制代码
public class OperandStackTest {
    public void testAndOperation(){
        byte i = 15;
        int j = 8;
        int k = i+j;
    }
}
java 复制代码
 0 bipush 15
 2 istore_1
 3 bipush 8
 5 istore_2
 6 iload_1
 7 iload_2
 8 iadd
 9 istore_3
10 return

bipush将15这个值push到了操作数栈中,此时我们的操作数栈就有了第一个值。我们需要回顾一下:byte、short、char、boolean、int类型在声明之后往数组中进行存放的时候都会保存为int类型。也就是说虽然定义的是byte类型,但是存放到数组中就是int类型

栈帧在调用之初,栈帧被创建完成,其中的操作数栈和局部变量表是空的。PC寄存器中存放着第一条要执行的指令的地址。

istore_1将这个值从操作数栈放到了局部变量表中索引为1的位置,为什么不是0呢?因为这不是一个静态方法,索引为零的位置存放的是this。

此时的操作数栈就成了空,这是一个出栈的操作。过程中会修改PC寄存器中的索引值为下一条命令的索引值。

过程中会修改PC寄存器中的索引值为下一条命令的索引值。

同样的道理,8也会经过bipush和istore_2,然后最终的结果如下:

iload_1和iload_2命令会将变量中索引为1,2的数据取出来分别放到局部变量表中

最终的运行结果如下:

紧接着会进行一个iadd命令,这个命令呢会使数据进行出栈,然后相加。值得注意的是,字节码指令需要被翻译为机器指令,机器指令操作CPU进行相加。然后将结果23放到操作数栈当中。

运行结果如下:

最终,istore_3将这个值从操作数栈放到了局部变量表中索引为3的位置, 此时的操作数栈就成了空,这是一个出栈的操作。过程中会修改PC寄存器中的索引值为下一条命令的索引值。

最终的运行结果如下:

我们也注意到,局部变量表长度为4,操作数栈深度为2(看javap的结果),这也是与图中可以对应上的,唯一区别的是局部变量表中的因为篇幅原因,this的位置也就是索引为0的变量槽没有展示出来。

补充说明:

我们注意到bipush是将一个byte类型的数据push到操作数栈中基于int类型进行存储,还有sipush,这个字节码指令的含义是将short类型的数据push到操作数栈中基于int类型进行存储。

如果方法有返回值,那么最终的字节码指令将由return会变成ireturn。也就是将值做了一个返回,这个栈帧就结束了,另外一个调用此方法的栈帧会立即调用一个aload_x这样的一个操作,将上一个方法的返回值加载到此栈帧的操作数栈中。

java 复制代码
    public int getSum(){
        int m = 10;
        int n = 20;
        int k = m+n;
        return k;
    }

    public void testGetSum(){
        //aload_0获取上一个栈帧返回的结果,并保存在操作数栈中。
        int i = getSum();
        int j = 10;
    }

2:面试题

i++ 和 ++j的区别是什么?

此问题,后续我们在字节码文章中会跟大家进行探讨

相关推荐
数巨小码人3 小时前
QT SQL框架及QSqlDatabase类
jvm·sql·qt
martian66512 小时前
【Java高级篇】——第16篇:高性能Java应用优化与调优
java·开发语言·jvm
李长渊哦19 小时前
Java 虚拟机(JVM)方法区详解
java·开发语言·jvm
二十七剑1 天前
jvm中各个参数的理解
java·jvm
七禾页话1 天前
垃圾回收知识点
java·开发语言·jvm
至少零下七度2 天前
Mac book Air M2 用VMware安装 Ubuntu22.04
linux·ubuntu·vmware·虚拟机
小梁不秃捏2 天前
深入浅出Java虚拟机(JVM)核心原理
java·开发语言·jvm
xiaolingting2 天前
JVM层面的JAVA类和实例(Klass-OOP)
java·jvm·oop·klass·instanceklass·class对象
神仙别闹2 天前
基于Python+Sqlite实现的选课系统
jvm·python·sqlite
上分小子2.03 天前
jvm-Java虚拟机
java·开发语言·jvm