JVM进阶之字节码指令解析(上篇)

本文详细介绍了JVM的加载与存储指令,包括局部变量入栈、常量入栈、出栈指令,以及操作数栈和局部变量表的工作原理。讲解了压栈指令如iload、fload、ldc等,和出栈指令如istore、lstore。还探讨了算术指令的基本概念、类型转换指令,包括宽化和窄化转换的细节,如i2l、i2f、l2i等,并通过实例说明了转换过程。
摘要由CSDN通过智能技术生成

一、加载与存储指令

1.作用

加载和存储指令,用于将数据从栈帧的局部变量表和操作数栈之间来回传递。

2.常用指令

  • 局部变量入栈指令:将一个局部变量加载到操作数栈,比如iload、fload、iload_
  • 常量入栈指令:将一个常量加载到操作数栈,比如bipush、sipush、ldc、ldc_w、1dc2_W、aconst_null、iconst_m1、iconst_*、lconst_、fconst_、dconst_
  • 出栈指令:将一个数值从操作数栈存储到局部变量表,比如xstore、xstore_、xastore
  • 扩充局部变量表的访问索引的指令::wide

💡【注意】:上面出现的iload_指令代表了iload_0、iload_1、iload_2和iload_3这几个指令,这几组指令都是某个带有一个操作数的通用指令(例如 iload)的特殊形式,对于这若干组特殊指令来说,它们表面上没有操作数,不需要进行取操作数的动作,但操作数都隐含在指令中。

3.操作数栈

  • Java字节码是Java虚拟机所使用的指令集,因此它与Java虚拟机基于栈的计算模型是密不可分的。在解释执行过程中,每当为Java方法分配栈桢时,Java虚拟机往往需要开辟一块额外的空间作为操作数栈,来存放计算的操作数以及返回结果。

  • 在执行指令之前,Java虚拟机要求该指令的操作数已被压入操作数栈中。在执行指令时,Java虚拟机会将该指令所需的操作数弹出,并且将指令的结果重新压入栈中。

    举例:完成1 + 2的算术运算

    这里使用的是加法指令iadd,在执行该指令前,栈顶的两个元素分别为整数1和整数2,那么执行iadd指令时会将这两个整数弹出栈,并将求得的和整数3压入栈。

    请添加图片描述

    请添加图片描述

4.局部变量表

  • Java方法栈桢的另外一个重要组成部分则是局部变量表,字节码程序可以将计算的结果缓存在局部变量表中。实际上,Java虚拟机将局部变量表作为一个数组,依次存放方法中所涉及的所有局部变量。
  • 在栈帧中,与性能调优关系最为密切的部分就是局部变量表,而且局部变量表中的变量也是重要的垃圾回收根节点,因为只要被局部变量表中直接或间接引用的对象都不会被回收。

举例

public void foo(long l, float f){
   
    {
   
        int i = 0;
    }
    {
   
        String s = "Hello, World";
    }
}

请添加图片描述

5.压栈指令

  • 局部变量压栈指令是将给定的局部变量表中的数据压入到操作数栈中。
  • 指令形式一般由:xload_(x为i、l、f、d、a,n为日到3)、xload (x为i、l、f、d、a),指令xload_n表示将第n个局部变量压入操作数栈,比如iload_1、fload_0、aload_0等指令。

代码举例

请添加图片描述

6.入栈指令

  • 常量入栈指令的功能是将常数压入操作数栈,根据数据类型和入栈内容的不同,又可以分为const系列指令、push系列指令和ldc系列指令。

  • const系列

    用于对特定的常量入栈,入栈的常量隐含在指令本身里,指令有: iconst_(i从-1到5)、lconst_(l从0到1)、fconst_(f从0到2)、dconst_(d从0到1)、aconst_null 。

  • push系列

    指令有:bipush、sipush,它们的区别在于接收数据类型的不同,bipush接收8位整数作为参数,sipush接收16位整数。

  • ldc系列

    ldc指令,它可以接收一个8位的参数,该参数指向常量池中的int、float或者String的索引,将指定的内容压入堆栈。类似的还有ldc_w,它接收两个8位参数。另外如果要压入的元素是long、double类型的,则使用ldc2_w指令。

数据类型与指令的对应关系

请添加图片描述

代码举例

请添加图片描述

请添加图片描述

7.出栈指令

  • 出栈装入局部变量表的指令用于将操作数栈中栈顶元素弹出后,装入局部变量表的指定位置,然后给局部变量赋值。
  • 这类指令主要以store的形式存在,比如:xstore、xstore_n
    • 指令istore_n将从操作数栈中弹出一个整数,并把它赋值给局部变量索引n位置。
    • 指令xstore由于没有隐含参数信息,故需要提供一个byte类型的参数类指定目标局部变量表的位置。
  • 我们看到xstore_n这样的命令需要带一个参数,用来指明将弹出的元素放在局部变量表的第几个位置。但是,为了尽可能压缩指令大小,使用专门的istore_1指令表示将弹出的元素放置在局部变量表第1个位置,类似的还有 istore_0、istore_2、istore_3,它们分别表示从操作数栈顶弹出一个元素,存放在局部变量表第0、2、3个位置。由于局部变量表前几个位置总是非常常用,因此这种做法虽然增加了指令数量,但是可以大大压缩生成的字节码的体积。如果局部变量表很大,需要存储的槽位大于3,那么可以使用istore指令,外加一个参数,用来表示需要存放的槽位位置。

代码举例

请添加图片描述

二、算术指令

1.基本概述

  • 算术指令用于对两个操作数栈上的值进行某种特定运算,并把计算结果重新压入操作数栈。
  • 大体上算术指令可以分为两种:对整型数据进行运算的指令和对浮点类型数据进行运算的指令。

2.实际类型和运算类型对应关系

请添加图片描述

3.指令分类

  • 加法指令:iadd、ladd、fadd、dadd

  • 减法指令:isub、lsub、fsub、dsub

  • 乘法指令:imu、lmu、fmul、dmul

  • 除法指令:idiv、ldiv、fdiv、ddiv

  • 求余指令:irem、lrem、frem、drem

  • 取反指令:ineg、lneg、fneg、dneg

  • 自增指令:iinc

  • 位运算指令:

    • 位移指令:ishl、ishr、 iushr、lshl、lshr、 lushr
    • 按位或指令:ior、lor
    • 按位与指令:iand、land
    • 按位异或指令:ixor、lxor
  • <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小吉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值