• i++和++i的区别


    栈的概述

    栈 Stack

    Each Java Virtual Machine thread has a private Java Virtual Mahine stack, created at the same time as the thread.

    A Java Virtual Machine stack stores frames

    栈里面装的是栈帧

    栈帧Frame——每个方法对应一个栈帧:

    A Frame used to store data  and partial results , as well  as to perform dynamic linking, return values for methods, and dispatch exceptions.

      1. Local Variable Table 局部变量表:当前栈帧用到的同步变量,栈帧一弹出就没了 ,类似CPU的寄存器,临时的存放一些数据,运算完成之后 把数据存回来。

      2. Operand Stack 操作数栈:类似寄存器的指令集对应的内存,从内存里面拿数据出来

      3. Dynamic Linking 动态链接:a()方法里调用了b()方法,a()代码里调用了B() b方法需要去常量池里找(这个link就叫 Dynamic Linking)

                https://blog.csdn.net/qq_41813060/article/details/88379473

      4. return address a()调用了b() b方法的返回值放在了什么地方

    指令集分为两种

      基于栈的指令集(JVM):比较简单 就是压栈出栈

      基于寄存器的指令集:(根据寄存器的数量大小, 复杂但是速度快)

    分析i++和++i

    下面一断代码 打印的结果是8

        public static void main(String[] args) {
            int i = 8;
            i = i++;
            System.out.println(i);
        }

    虚拟机栈的运行和指令脱不开:下面贴出指令集

     0 bipush 8   //把8扔到栈里面去 压栈
     2 istore_1  //把8弹出来放到i里面(i在局部变量为1的位置)  这个时候 复制操作i=8; 完成
     3 iload_1  //把局部变量表为1位置上的数拿出来 压栈
     4 iinc 1 by 1 //把局部变量表为1的位置上的数加1。(i++在局部变量表中完成)
     7 istore_1  // 把8弹出来 局部变量表的值从9变成8
     8 getstatic #2 <java/lang/System.out>
    11 iload_1
    12 invokevirtual #3 <java/io/PrintStream.println>
    15 return

    最后打印结果 8

    如果把i=i++ 改成i=++i;在分析一断指令 打印结果是9

     0 bipush 8  //压栈
     2 istore_1 //把8弹到局部变量表
     3 iinc 1 by 1 //局部变量表1的位置加1 完成i++操作
     6 iload_1 //把局部变量表为1的位置拿出来 压栈 栈里面是9
     7 istore_1 //再把9弹出来 赋值给i i是9
     8 getstatic #2 <java/lang/System.out>
    11 iload_1
    12 invokevirtual #3 <java/io/PrintStream.println>
    15 return

    注意 第三步和第四步:

    i++是先压栈,在局部变量表加1,然后栈里的数据弹出

    ++i是 局部变量表加1 在压栈,然后栈里的数据弹出

    分析方法调用字节码

    public class TestIPulsPlus {
        public static void main(String[] args) {
            TestIPulsPlus t= new TestIPulsPlus();
            t.m();
        }
    
        int m(){
            return 100;
        }
    }

    此时由于main()调用了m()方法,此时线程栈里面有两个栈帧

    下面分析指令

     0 new #2 <com/mashibing/jvm/c4_RuntimeDataAreaAndInstructionSet/TestIPulsPlus> //先new出来这个对象 对象的地址压栈
     3 dup // 把栈顶上的地址在复制一个 此时栈里面有两个地址都指向new出来的对象
     4 invokespecial #3 <com/mashibing/jvm/c4_RuntimeDataAreaAndInstructionSet/TestIPulsPlus.<init>> //执行默认的构造方法 ,把上面的弹出去 做运算
     7 astore_1 //出栈 new完的之后赋值给t 
     8 aload_1 //把t压栈
     9 invokevirtual #4 <com/mashibing/jvm/c4_RuntimeDataAreaAndInstructionSet/TestIPulsPlus.m>//调用m1()方法 t弹栈 m1去下一个栈帧执行
    12 pop //m()方法返回的时候 往main() 方法的栈帧 栈顶上放了一个100 main方法不管这个100有没有用 先把它弹出来
    13 return

     m1()的byteCode

    0 bipush 100
    2 istore_1
    3 return //回到上面 9的位置继续执行

    看看递归的字节码

    代码

    public class Hello_04 {
        public static void main(String[] args) {
            Hello_04 h = new Hello_04();
            int i = h.m(3);
        }
    
        public int m(int n) {
            if(n == 1) return 1;
            return n * m(n-1);
        }
    }

    m()方法的字节码。此时JVM栈里面 会有main m1 m2 m3 这四个栈帧

     0 iload_1           //把3压栈
     1 iconst_1  //把用到的数字1 压栈
     2 if_icmpne 7 (+5)  //如果不等 跳到第七条指令
     5 iconst_1
     6 ireturn
     7 iload_1  //把3load进来
     8 aload_0 //this引用load进来
     9 iload_1 //把3 扔进来
    10 iconst_1 //把1扔进来 (执行3-1)
    11 isub  //把3和1弹出去
    12 invokevirtual #4 <com/mashibing/jvm/c4_RuntimeDataAreaAndInstructionSet/Hello_04.m> 调用m方法 参数是2
    15 imul //返回完成之后进行相乘 相乘完之后 才会依次返回(递归)
    16 ireturn
  • 相关阅读:
    excel复杂表头导出
    搜索联想词(提示词)实现
    做题常用容器及方法
    课外加餐:1 | 浏览上下文组:如何计算 Chrome 中渲染进程的个数?
    浏览器安全:36 | HTTPS:让数据传输更安全
    课外加餐 2 | 任务调度:有了 setTimeOut,为什么还要使用 rAF?
    springBoot 配置SwaggerUi接口测试
    jmeter报错javax.script.ScriptException: UnicodeEncodeError: 'ascii' codec can't encode characters in position 109111: ordinal not in range(128) in <script> at line number 3
    websocketpython
    测试
  • 原文地址:https://www.cnblogs.com/ssskkk/p/12861321.html
Copyright © 2020-2023  润新知