入门篇-其之六-附录一-以Java字节码的角度分析i++和++i

这篇具有很好参考价值的文章主要介绍了入门篇-其之六-附录一-以Java字节码的角度分析i++和++i。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:众所周知,i++++i的区别是:i++先将i的值赋值给变量,再将i的值自增1;而++i则是先将i的值自增1,再将结果赋值给变量。因此,二者最终都给i自增了1,只是方式不同而已。

当然,如果在面试过程中面试官问你这个问题,只回答出上述内容,只能说明你对这方面的知识了解的还是太浅显。那么i++++i到底有什么不同之处呢?

一、局部变量表与操作数栈简介

《深入理解Java虚拟机》第八章对栈帧结构有如下描述Java虚拟机以方法作为最基本的执行单元,“栈帧”(Stack Frame)则是用于支持虚拟机进行方法调用和方法执行背后的数据结构,它也是虚拟机运行时数据区中的虚拟机栈的栈元素。

在一个活动线程中,可能会执行多个方法,因此会存在多个栈帧,和“栈”(先进后出)一样,处于栈顶的栈帧才是真正运行的,处于栈顶的栈帧称作“当前栈帧”(Current Stack Frame),这个栈帧所属的方法称作“当前方法”(Current Method)。

在执行main方法时,main方法所属的线程主线程,假设在主线程中调用了一个method1()方法,在method1()内部调用了method2()方法,在method2()方法执行两个整数运算,示例如下:

/**
 * 方法调用
 *
 * @author iCode504
 * @date 2023-10-23 22:05
 */
public class StackFrameDemo1 {
    public static void main(String[] args) {
        System.out.println("main开始执行");
        method1();
        System.out.println("main执行完成");
    }

    private static void method1() {
        System.out.println("method1开始执行");
        int result = method2();
        System.out.println("result = " + result);
        System.out.println("method1执行结束");
    }

    private static int method2() {
        int var1 = 10;
        int var2 = 20;
        return var1 + var2;
    }
}

运行结果:

由代码我们可以看出,main方法最先执行一个输出,然后进入method1执行第一个输出,再完整执行method2method2执行完成以后,再执行method1,最后执行main方法,由于这段代码中只涉及一个主线程,并且最先完整执行方法的是method2,因此method2对应的栈帧就是当前栈帧,main方法最后执行完毕,因此main方法对应的栈帧在method2method1之下。以下是这段代码对应的栈帧概念图:

在每一个栈帧中存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息

1.1 局部变量表

局部变量表(Local variable Table)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。

局部变量表的容量是以变量槽(Variable Slot)为最小单位,每个变量槽能存储基本数据类型和引用数据类型的数据。为了尽可能节省栈帧消耗的内存空间,局部变量表中的变量槽是可以重用的。

JVM使用索引定位的方式使用索引变量表,索引值的范围是从0开始到局部变量表最大变量槽的数量(类似数组结构)。

当一个方法被调用的时候,JVM会使用局部变量表来完成参数值到参数变量列表的传递,即实参到形参的传递。

1.2 操作数栈

操作数栈(Operand Stack)也称作操作数栈,它是一个栈结构(后进先出,例如手枪的弹夹,先打出去的子弹是最顶上的子弹)。

在方法开始执行的时候,这个方法对应的操作数栈是空的,在方法执行过程中,会有各种字节码指令向操作数栈中写入或读取内容,即出栈和入栈操作,例如:两数相加运算时,就需要将两个数压入栈顶后调用运算指令。

操作数栈中的元素的数据类型必须和字节码指令序列严格匹配,在编译程序代码的时候编译器必须要严格保证这一点,在类的校验阶段的数据流分析时候还需要再次校验。例如:执行加法iaddiint类型,add是两个数相加)命令时,就需要保证两个操作数必须是int类型,不能出现其他类型相加的情况。

二、字节码分析(图解)

我们可以从字节码的角度进一步对i++++i的执行过程做进一步的分析。以下面代码为例:

/**
 * i++和++i的深入分析
 * 
 * @author iCode504
 * @date 2023-10-17 5:58
 */
public class IncrementAndDecrementOperators2 {
    public static void main(String[] args) {
        int intValue1 = 2;
        int intValue2 = 2;
        int result1 = intValue1++;
        int result2 = ++intValue2;
        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
    }
}

我们需要查看编译后的字节码文件,字节码文件不能直接使用记事本打开,但是我们可以使用javap -verbose 文件名.class命令,以IncrementAndDecrementOperators2.class为例:

javap -verbose IncrementAndDecrementOperators2.class

此时就会打开所有的字节码文件,我们只需要关注main方法内的执行过程即可:

首先来解释一下这四行代码的含义:

0: iconst_2
1: istore_1
2: iconst_2
3: istore_2
  • iconst_2一共有两部分组成,i指的是int类型(源代码中我们定义的确实是int类型),const代表常量(数字2是整型常量),iconst_2的含义是将2入操作数栈。
  • istore_1中的store代表的是存储,istore_1的含义是将操作数栈中的数值2出栈,存入到局部变量表1的位置。同理,i_store2表示将操作数栈中的数值2出栈,存储到局部变量表2的位置。

以下是前面四行代码存储过程图(存储过程全部流程图点击此链接下载:点我下载):

此时我们继续观察4-8行代码:

4: iload_1
5: iinc			1, 1
8: istore_3
  • iload_1的作用是将局部变量表1号位置存储的值移动到操作数栈的栈顶。
  • 第5行的iinc有两个参数,第一个参数1是局部变量表的位置,另一个参数1的含义是在该位置存储一个1,如果这个位置存在值,那么这个值的结果是已存在值 + 参数值
  • istore_3将操作数栈中的数移动到局部变量表的3号位置。

以下是这三行代码的示意图:

9-12行的字节码的作用原理和4-8行的作用原理基本相同:

9: iinc			2, 1
12: iload_2
13: istore		4

istore 4的作用是将操作数栈中的值存储到局部变量表4号位置。

以下是这三行代码的示意图:

接下来15-30行是和系统输出有关的。其中第30行iload_3在局部变量表中(这个值为2)值移动到操作数栈顶供系统输出,事实上iload_3的值正好对应源代码中变量result1的值。也就是说,result1输出结果就是iload_3的数值2。

同理,iload 4就是第二个要输出的值,在局部变量表中第4个位置存储的值正好是3,而输出的变量名是result2,因此result2的输出结果是3。

三、i++和++i性能分析

i++++i主要用在普通for循环上,那么我们就将二者用在for循环上,循环相同的次数,从字节码的角度进行分析。

以下是使用i++++i的两个for循环文件:

/**
 * i++在for循环的使用
 *
 * @author ZhaoCong
 * @date 2023-10-21 16:14:33
 */
public class LoopTest1 {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {

        }
    }
}
/**
 * ++i在for循环的使用
 *
 * @author ZhaoCong
 * @date 2023-10-21 16:15:17
 */
public class LoopTest2 {
    public static void main(String[] args) {
        for (int i = 0; i < 100; ++i) {

        }
    }
}

执行编译命令以后,我们来查看两个文件的字节码:

仔细观察这两个字节码文件内容,我们发现在两个文件main方法的字节码内容完全相同。由此可见,两种方式执行for循环的效率是相同的。文章来源地址https://www.toymoban.com/news/detail-711456.html

到了这里,关于入门篇-其之六-附录一-以Java字节码的角度分析i++和++i的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 入门篇-其之九-流程控制之条件判断

    本文中使用到的工具是Intellij IDEA和JDK 8,需要安装两款工具的请查看这两篇教程:点我查看安装JDK8/11/17教程、点我查看安装Intellij IDEA教程。 前面我们写过的代码,都是在 main 方法中自上到下按顺序执行的,举一个代码栗子: 这段代码就是先定义西瓜的单价、再定义西瓜的重

    2024年02月05日
    浏览(37)
  • 对一手游的自定义 luajit 字节码的研究

    最近闲下来之后无聊研究起了一个unity手游 大量使用了 lua (或者说就是 lua 写的 ) 看到网上已有的一些针对方案 都觉得太不方便 于是深入研究了一下 他自定义的 luajit 首先 这是一个 unity的 传统手游 这里就跳过较为前期的部分 像是 libtersafe . libbugly . libcri_ware 这些都是老熟

    2024年01月22日
    浏览(41)
  • 【Linux系统基础快速入门详解】find与指纹多角度分析与解决网站页面恶意修改

    鱼弦:CSDN内容合伙人、CSDN新星导师、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen) 原理详细解释: 指纹分析:指纹分析是一种通过对网站页面进行多角度的特征提取、比对和分析,以识别和解决网站页面恶意修改的方

    2024年02月07日
    浏览(48)
  • 深入理解JVM虚拟机第二篇:虚拟机概念和JVM整体架构以及字节码的执行路线

      😉😉 学习交流群: ✅✅1:这是孙哥suns给大家的福利! ✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 🥭🥭3:QQ群:583783824   📚📚  工作微信:BigTreeJava 拉你进微信群,免费领取! 🍎🍎4:本文章内容出自上述:Spring应用课程!💞💞

    2024年02月09日
    浏览(56)
  • Java字节码编程:从入门到精通

      Java是一种高级语言,其代码在编译后被转换为Java字节码文件。Java字节码文件包含了Java平台上的指令集,这些指令可以在Java虚拟机(JVM)上执行。因此,通过编写和操作字节码,我们可以在运行时动态地修改和扩展Java应用程序的行为。   Java字节码是Java编译器生成的

    2024年02月08日
    浏览(52)
  • ASM Java字节码操作框架入门学习 输出Hello World

    查看字节码信息 方法调用 invokestatic:用于调用静态方法。该指令会根据方法的类名、方法名和方法描述符进行方法查找和调用。 invokespecial:用于调用私有方法、构造方法和父类方法。该指令会根据方法的类名、方法名和方法描述符进行方法查找和调用。 invokevirtual:用于调

    2024年02月13日
    浏览(47)
  • 【Java可执行命令】(十二)依赖分析工具jdeps:通过静态分析字节码并提取相关信息来实现依赖分析 ~

    Java中的 jdeps 命令是一个用于分析类或 JAR 文件的工具,它能够帮助开发者识别出类之间的依赖关系。 jdeps 命令最早于Java 8版本中引入,旨在帮助开发者识别出Java类之间的依赖关系。其设计目的是为了帮助开发者在进行代码重构、迁移、与外部库集成等操作时,更好地了解库

    2024年02月14日
    浏览(79)
  • 第5章附录4:本章函数速查表(MATLAB入门课程)

    ​讲解视频:可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇(数学建模清风主讲,适合零基础同学观看)_哔哩哔哩_bilibili 本章知识点非常多,也介绍了非常多的内置函数。下面根据函数的功能提供了几张速查表,这能帮助大家快

    2024年02月03日
    浏览(39)
  • Java入门高频考查基础知识4(字节跳动面试题18题2.5万字参考答案)

    Java 是一种广泛使用的面向对象编程语言,在软件开发领域有着重要的地位。Java 提供了丰富的库和强大的特性,适用于多种应用场景,包括企业应用、移动应用、嵌入式系统等。          以下是几个面试技巧:    1. 复习核心概念: 回顾 Java 的核心概念,如面向对象编

    2024年01月18日
    浏览(57)
  • 【Java基础教程】(四十四)IO篇 · 上:File类、字节流与字符流,分析字节输出流、字节输入流、字符输出流和字符输入流的区别~

    掌握 java.io包中类的继承关系 ; 掌握 File类的使用,并且可以通过File类进行文件的创建、删除以及文件夹的列表等操作; 掌握字节流或字符流操作文件内容,字节流与字符流的区别; 在 java.io 包中,如果要进行文件自身的操作 (例如:创建、删除等), 只能依靠 java.io.File 类完

    2024年02月15日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包