Java的i = i++与i=++i的去呗从JVM指令讲解图文并茂,一文必懂

简介: Java的i = i++与i=++i的去呗从JVM指令讲解图文并茂,一文必懂

一、Java的i = i++深入JVM指令讲解 图文并茂 看了必懂

先看一段简单的代码

package com.zhou.jvm.runtimedataAreainstructionset;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/5-19:55
 */
public class TestPlusPlus {
    public static void main(String[] args) {
        int i = 8;
        i = i++;
        System.out.println(i);
    }
}

最后程序打印的结果是8不是9

那么这个是为什么呢?我们从JVM指令来看

编译这个类之后我们用jclasslib插件show bytecode with jclasslib,可以看到如下

我把里面的Bytecode单独拿出来,粘在这里

0 bipush 8
 2 istore_1
 3 iload_1
 4 iinc 1 by 1
 7 istore_1
 8 getstatic #2 <java/lang/System.out>
11 iload_1
12 invokevirtual #3 <java/io/PrintStream.println>
15 return

接下来一条一条指令的解析

1. bipush 8

第一步,bipush指令具体的含义就是把一个int类型的数据压入操作数栈。具体的可以参照官网链接,如下图所示把8压入了操作数栈

2. istore_1

第二步,istore_1就是将操作数栈的数字弹出栈并写入局部变量表当中建立的一个index值为1的索引的变量,这里程序的i变量此时就为8,完成了int i = 8的操作。

3. iload_1

第三步,iload_1指令就是将局部变量表中的值再次压入操作数栈当中去。所以此时操作数栈里面的值此时为8

4. iinc 1 by 1

第四步,iinc 1 by 1就是将局部变量表中索引值为1的变量加一,就是完成了i++这一步操作,此时局部变量表中的i为9

5. istore_1

第五步,istore_1就是将操作数栈的数字弹出栈并写入局部变量表当中建立的一个index值为1的索引的变量,这里程序的i变量此时就为8,完成了int i = i++的操作。

6. iload_1

因此最后输出的就是8

二、换成i=++i的JVM指令研究

package com.zhou.jvm.runtimedataAreainstructionset;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/5-19:55
 */
public class TestPlusPlus {
    public static void main(String[] args) {
        int i = 8;
        i = ++i;
        System.out.println(i);
    }
}

这个类的Bytecode

i=++i的bytecode

0 bipush 8
 2 istore_1
 3 iinc 1 by 1
 6 iload_1
 7 istore_1
 8 getstatic #2 <java/lang/System.out>
11 iload_1
12 invokevirtual #3 <java/io/PrintStream.println>
15 return

把i = i++的bytecode拿出来对比

0 bipush 8
 2 istore_1
 3 iload_1
 4 iinc 1 by 1
 7 istore_1
 8 getstatic #2 <java/lang/System.out>
11 iload_1
12 invokevirtual #3 <java/io/PrintStream.println>
15 return

可以看出两者之间的区别在于 iload_1 和 iinc 1 by 1的顺序问题

i=++i的是先 iinc 1 by 1再 iload_1,就是说先对局部变量表的变量进行自增再把这个自增以后的值压入操作数栈,自然这个操作数栈的数据已经+1了

i=i++的是先iload_1 再iinc 1 by 1,就是说先把局部变量表中的变量的值压入操作数栈,再进行自增操作,所以这个对于后续的istore_1操作有影响,使得这个最终的局部变量表中的数据还是之前没有加1的那个数值。


相关文章
|
1月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
62 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
1月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
28 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
20天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
1月前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
26 3
|
30天前
|
存储 算法 Java
深入理解Java虚拟机(JVM)及其优化策略
【10月更文挑战第10天】深入理解Java虚拟机(JVM)及其优化策略
41 1
|
1月前
|
安全 Java API
🌟探索Java宇宙:深入理解Java技术体系与JVM的奥秘
本文深入探讨了Java技术体系的全貌,从Java语言的概述到其优点,再到Java技术体系的构成,以及JVM的角色。旨在帮助Java开发者全面了解Java生态,提升对Java技术的认知,从而在编程实践中更好地发挥Java的优势。关键词:Java, JVM, 技术体系, 编程语言, 跨平台, 内存管理。
34 2
|
1月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
38 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
1月前
|
监控 Java
Java的JVM如何优化?
Java的JVM如何优化?
54 3
|
6天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
2天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
15 9