从 i++ 和 ++i 说起局部变量表和操作数栈
本文转载自:从 i++ 和 ++i 说起局部变量表和操作数栈
最近公司有人看了尚硅谷柴林燕老师的第一季面试题,就想来考考我。我觉得柴老师讲的很好,部分内容可以延伸一下,所以写这篇文章分享给大家!
这篇文章涉及到了一点 JVM 方面的知识。面试时可能也会遇到,所以认真看不会吃亏!
int i = 1;
i = i++;
int j = i++;
int k = i + ++i * i++;
// 业余草:www.xttblog.com
System.out.println("i = " + i);
System.out.println("j = " + j);
System.out.println("k = " + k);
运行结果是:
i = 4
j = 1
k = 11
为什么是这个结果呢?我们先来看看 Java 虚拟机栈的知识点。
Java 内存可以粗糙的区分为堆内存(Heap)和栈而不是队列呢?很容易理解,因为方法内存(Stack),其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。
至于为什么是栈而不是队列呢?很容易理解,因为方法调用链是最后一个调用先返回的。用队列显然不合适。
Java 虚拟机栈也是线程私有的,生命周期与线程相同。任何一个方法的执行也都是一个线程来调用的,所以前面这句话就很好理解了。
每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用、动态链接、程序出口等信息。每一个方法从调用到执行完成的过程,对应一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。

局部变量表存放了编译器可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、对象引用(reference类型,不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddressleixing (字节码指令地址)。局部变量表所需内存在编译期间完成分配,运行期间不会改变。
和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。
虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double、reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。
begin
iload_0 // push the int in local variable 0 onto the stack
iload_1 // push the int in local variable 1 onto the stack
iadd // pop two ints, add them, push result
istore_2 // pop int, store into local variable 2
end
在这个字节码序列里,前两个指令 iload_0 和 iload_1 将存储在局部变量中索引为 0 和 1 的整数压入操作数栈中,其后 iadd 指令从操作数栈中弹出那两个整数相加,再将结果压入操作数栈。第四条指令 istore_2 则从操作数栈中弹出结果,并把它存储到局部变量区索引为 2 的位置。
0 iconst_1
1 istore_1
2 iload_1
3 iinc 1 by 1
6 istore_1
7 iload_1
8 iinc 1 by 1
11 istore_2
12 iload_1
13 iinc 1 by 1
16 iload_1
17 iload_1
18 iinc 1 by 1
21 imul
22 iadd
23 istore_3
所以,我们现在解释一下上面的代码。int i = 1;发生了两个过程,iconst_1 是将 int 型的 1 推送至栈顶。istore_1 把栈顶的元素弹出,并赋值给局部变量表中位置为“1”的变量,此时指变量i。这两句就相当于 int i = 1;
i = i++; 代码解释:iload_1 把局部变量表中位置为“1”的变量加载到栈顶,即把 i 的值加载到栈顶。iinc 1 by 1,将局部变量表中位置为“1”的 i 加 1,此时局部变量表中 i 的结果为 2。然后 istore_1 把栈顶的元素弹出,并赋值给局部变量表中位置为“1”的变量。所以 i 的值又被改为了 1。
int j = i++; 代码解释:iload_1 把局部变量表中位置为“1”的变量加载到栈顶,即把 i 的值加载到栈顶。iinc 1 by 1 将局部变量表中位置为“1”的 i 加 1,此时结果为 2,也就是局部变量表中 i 的结果为 2。istore_2 把栈顶的元素弹出并赋值给局部变量表中位置为“2”的 j。所以 j 是 1,但是 i 的值已经为 2。
int k = i + ++i * i++; 这个是最复杂的,我们直接看 JVM 指令即可。iload_1 把局部变量表中位置为“1”的变量加载到栈顶,即把 i 的值加载到栈顶,注意 i 的值此时是 2。iinc 1 by 1,i 自增,然后 i 就变成 3 了。接着两个 iload_1、iload_1分别把局部变量 i 压到栈了。所以栈中现在是 3、3、2。然后执行 iinc 1 by 1,i 又自增了,这时把局部变量表中的 i 就变成 4 了,注意这个 4 并未压入栈。之后 imul 进行乘法计算,栈中的前两个元素计算后是 9,之后执行 iadd 指令,也就是 9 + 2,结果为 11。最后 istore_3 把 11 从栈顶弹出,并赋值给 k,也就是局部变量表中位置为“3”的 k 的值是 11。
后面的 JVM 指令,我们就不用看了,都是打印变量到控制台中。
综上,Java 中的局部变量表和操作数栈非常的重要。下面我们通过一张图片来看看局部变量表和操作数栈之间的操作关系。

从 i++ 和 ++i 说起局部变量表和操作数栈的更多相关文章
- JVM探秘6--图解虚拟机栈的局部变量表和操作数栈工作流程
案例代码如下: public class JVMTest { public static Integer num = 10; public int add(int i){ int j = 5; int ...
- Java中的局部变量表及使用jclasslib进行查看
直接上下载地址 jclasslib是一个独立的工具,不是包含在JDK中的工具,需要自己进行下载,下载地址如下: http://downfile.downcc.com/down/JClassLib_wi ...
- JVM-栈帧之局部变量表
1.栈帧的内部结构 每个栈帧中存储着: 局部变量表(Local Variables) 操作数栈(Operand Stack)(或表达式栈) 动态链接(Dynamic Linking)(或指向运行时常量 ...
- JAVA 局部变量表
1. 除了 long,double 占用两个slot 之外,其他类型均占用一个slot. 2.在内容相同的情况下, 实例方法(不加 static) 会比 类方法 (static)对占用一个局部变量位置 ...
- java虚拟机 jvm 局部变量表实战
java局部变量表是栈帧重要组中部分之一.他主要保存函数的参数以及局部的变量信息.局部变量表中的变量作用域是当前调用的函数.函数调用结束后,随着函数栈帧的销毁.局部变量表也会随之销毁,释放空间. 由于 ...
- JVM 栈帧之操作数栈与局部变量表
目录 前置知识 引子 基于寄存器的设计模式 基于栈的设计模式 一个简单的例子 如何查看局部变量表? 实例方法中的局部变量表 结论 前置知识 阅读本文需要对以下知识有所了解: * 栈 * 汇编 * Ja ...
- 局部变量表中Slot复用对垃圾回收的影响详解
看两段代码 1. package com.jvm; public class Test { public static void main(String[] args) { { byte[] plac ...
- 栈帧的内部结构--局部变量表(Local Variables)
每个栈帧中包含: 局部变量表(Local Variables) 操作数栈(Opreand Stack) 或表达式栈 动态链接 (Dynamic Linking) (或指向运行时常量的方法引用) 动态返 ...
- C++模板实现动态顺序表(更深层次的深浅拷贝)与基于顺序表的简单栈的实现
前面介绍的模板有关知识大部分都是用顺序表来举例的,现在我们就专门用模板来实现顺序表,其中的很多操作都和之前没有多大区别,只是有几个比较重要的知识点需要做专门的详解. #pragma once #inc ...
随机推荐
- Ubuntu 18.04 切换使用Python3
我安装的Ubuntu 默认的python是2.7.5 python -V 我参考网上照到的文章,如果需要默认python为 python3 python命令默认是 python 3 sudo cp / ...
- node-express(1)建立post、get、跨域问题解决方案
首先下载express:npm i express let ess=require('express'); let app=ess(); let bodyParser=require('body-pa ...
- web storem破解
话不多说 直接跳地址 http://idea.lanyus.com/ 再加个附带汉化包,使用方法已经放在压缩包里面 网盘链接: https://pan.baidu.com/s/1aEA6SSbDuRg ...
- AES加密php,java,.net三种语言同步实现加密、解密
话不多数上代码: java::: /* * To change this license header, choose License Headers in Project Properties. * ...
- SQL Server SQLBindCol
说明 编辑 版本引入:ODBC 1.0 遵从标准:ISO 92 功能说明: SQLBindCol将应用程序的数据缓冲绑定到结果集的各列 函数原型: SQLRETURN SQLBindCol( SQLH ...
- StringUtils工具类常用api <转>
该工具类是用于操作Java.lang.String类的. StringUtils类与String类的区别在于:此类是null安全的,即如果输入参数String为null,则不会抛出NullPointe ...
- Java获取properties配置文件信息
调用方法:String url = PropertiesUtil.getProperty("url"); public class PropertiesUtil { public ...
- 阿里云POLARDB如何帮助百胜软件应对数据库的“巅峰时刻”
POLARDB是阿里云自研的下一代关系型云数据库,100%兼容MySQL,存储容量最高可达100TB,性能最高提升至MySQL的6倍,适用于企业多样化的数据库应用场景.POLARDB采用存储和计算分离 ...
- [学习笔记] $Maximum$ $Minimum$ $identity$
\(Maximum\) \(Minimum\) \(identity\)学习笔记 比较好玩的一个科技.具体来说就是\(max(a,b)=a+b-min(a,b)\),这个式子是比较显然的,但是这个可以 ...
- LUOGU P1501 [国家集训队]Tree II (lct)
传送门 解题思路 \(lct\),比较模板的一道题,路径加和乘的维护标记与线段树\(2\)差不多,然后剩下就没啥了.但调了我将近一下午.. 代码 #include<iostream> #i ...