jvm学习笔记:栈帧
栈帧内的数据结构
局部变量表(Local Variables):记录非静态方法的this指针、方法参数、局部变量
操作数栈(Operand Stack):用于计算的栈结构
动态链接(Dynamic Link):指向运行时常量池的方法引用
方法返回地址(Return Address):方法正常退出或异常退出的定义,以及方法间返回值传递(注意: java中的方法除了正常返回还可能因为异常退出,所以方法返回地址不一定就是方法退出的地址)
附加信息
局部变量表
Each frame contains an array of variables known as its local variables. The length of the local variable array of a frame is determined at compile-time and supplied in the binary representation of a class or interface along with the code for the method associated with the frame
主要存储方法的参数、方法内的局部变量,数据类型包括基本数据类型、对象引用(数值地址)、返回地址。局部变量表具有以下特点:
- 局部变量表数组的容量大小在编译时期确定,后面不会变化
- 局部变量表是每个线程私有的,不存在线程安全问题
非静态方法的局部变量表
- 构造方法、实例方法会自带一个this引用变量
- static方法不能使用this的原因:静态方法的局部变量表中不带有this的引用
变量槽(Slot)
局部变量表中最基本的存储单元,每个槽大小32位
- 局部变量表是index0开始的数组,数组的每个元素我们称为变量槽
- 局部变量表中,32位以内类型占一个槽(int),64位占两个槽(long、double),引用类型占用一个槽
- 调用占有两个槽的变量使用起始索引
slot重复利用
栈帧中局部变量表的槽位是可以复用的,如果某个局部变量的作用域已经结束了,那么在它后面声明的局部变量可以使用它的槽位
这样的设计使局部变量表的空间利用率更高,不会造成空位的情况。
重复利用的例子:
public void test1(){
int a = 0;
{
int b = 0;
b = a + 1;
}
int c = 1;
}
上面代码的局部变量表如图,可见b和c的槽位都是index=2,也就是说b的作用域结束后,c会重复使用b的槽位。
局部变量表与垃圾回收
- 局部变量表中的变量是重要的垃圾回收节点,只有被局部变量表中直接或者间接引用的对象才不会被回收
操作数栈(Operand Stack)
Each frame contains a last-in-first-out (LIFO) stack known as its operand stack. The maximum depth of the operand stack of a frame is determined at compile-time and is supplied along with the code for the method associated with the frame
操作数栈,在方法执行的过程中根据字节码指令,往栈中写入或提取数据
- 某些字节码指令将值压入操作数栈,其他指令又可以取出数值,使用后又可以再压入栈
- 比如:复制操作、交换、求和
- 字节码指令由执行引擎翻译成机器指令
- 操作数栈是一个栈帧中的结构,它随着一个方法的开始执行而被创建
- 每一个操作数栈都在编译时确定了固定的深度,这个深度保存在code属性的max_stack值
- 32位占一个栈深度、64位占两个栈深度
i++和++i区别
情景1
public void test(){
int i1 = 10;
i1++;
int i2 = 20;
++i2;
}
上面代码编译的字节码如上图,可见i++和++i编译出来的字节码其实是一样的,首先都是bipush指令将i的初值压入操作数栈,然后 用istore指令出栈并存入局部变量表。然后用iinc指令将局部变量表中的数据取出并+1
情景2
public void test(){
int i1 = 10;
int d1 = i1++;
int i2 = 20;
int d2 = ++i2;
}
上面代码编译后字节码如图,可以发现用i++赋值和++i赋值的字节码是有差异的。
int d = i++的情况:首先还是用 bipush指令把初始值放入操作数栈,然后用istore取出并存入局部变量表。在给d赋值时,会先用 iload指令读取局部变量表的 i 到操作数栈(暂时存在栈中,不出栈),然后用 iinc指令为局部变量表的 i 加1,完成后再用istore指令将操作数栈中没有加一的 i 出栈,存到新的局部变量表槽中。
int d = ++i的情况:与上述情况唯一的不同是,这种情况会先调用 iinc 指令将局部变量表的 i 加一,然后再用 iload指令将加一后的 i存入操作数栈,最后用 istore赋值给局部变量
情景3(丧心病狂)
public void test(){
int i1 = 10;
i1 = i1++;
int i2 = 10;
i2 = ++i2;
}
上述代码的字节码如上图
i = i ++情况:首先使用了 bipush 将10 压入了操作数栈,然后使用了 istore将初值10出栈并存入局部变量表,然后用 iload指令将局部变量 10 入栈到了操作数栈(接下来的操作该数值没改变)。接着使用了 iinc 指令将局部变量 i 取出、入栈、加一、出栈、存入局部变量表。这一过程没有改变最初入栈的初值10。最后,使用了 istore 指令将这个 10 覆盖了之前加一的局部变量。所以这种情况其实 i 的值没变。
i = ++i情况:同样,还是先用bipush、istore将初值10存入了局部变量表。因为++再i之前,所以先使用了 iinc 指令将局部变量表中的 10+1 改成了 11,然后再用 iload、istore指令来给 i 赋值,最终i会变成 11
其实通过上面两种情况的分析,我们可以发现,第二种情况下最后的iload、istore指令其实是没有实际意义的,它们只是将 i 入栈操作数栈又出栈存入原来的局部变量表位置。
动态链接(Dynamic Linking)
Each frame contains a reference to the run-time constant pool for the type of the current method to support dynamic linking of the method code
每个栈帧中包含的指向运行时常量池中该栈帧所属方法的引用
- 指向运行时常量池的方法引用
- 动态链接的作用就是将符号引用转换为直接引用
jvm学习笔记:栈帧的更多相关文章
- java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
- java之jvm学习笔记三(Class文件检验器)
java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...
- java之jvm学习笔记十三(jvm基本结构)
java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...
- JVM学习笔记-JVM模型
JVM学习笔记 == 标签(空格分隔): jvm 学习笔记全部来自于<深入理解java虚拟机>总结 jvm内存示意图 虚拟机栈(Java Virtual Machine Stacks): ...
- JVM学习笔记——字节码指令
JVM学习笔记——字节码指令 字节码 0与 1是计算机仅能识别的信号,经过0和1的不同组合产生了数字之上的操作.另外,通过不同的组合亦产生了各种字符.同样,可以通过不同的组合产生不同的机器指令.在不同 ...
- JVM学习笔记-第六章-类文件结构
JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...
- JVM学习笔记——内存结构篇
JVM学习笔记--内存结构篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的内存结构部分 我们会分为以下几部分进行介绍: JVM整体介绍 程序计数器 虚拟机栈 本地方法栈 堆 方法 ...
- JVM学习笔记——类加载和字节码技术篇
JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...
- java之jvm学习笔记四(安全管理器)
java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...
- 【Java虚拟机】JVM学习笔记之GC
JVM学习笔记二之GC GC即垃圾回收,在C++中垃圾回收由程序员自己来做,例如可以用free和delete来回收对象.而在Java中,JVM替程序员来执行垃圾回收的工作,下面看看GC的详细原理和执行 ...
随机推荐
- Jupyter Kernel Error 解决办法
首先在pycharm的terminal中输入 python -m ipykernel install --user 之后会如下图显示: 这就是kernel的安装路径. 然后输入: jupyter ke ...
- netty系列之:中国加油
目录 简介 场景规划 启动Server 启动客户端 消息处理 消息处理中的陷阱 总结 简介 之前的系列文章中我们学到了netty的基本结构和工作原理,各位小伙伴一定按捺不住心中的喜悦,想要开始手写代码 ...
- Netty基础招式——ChannelHandler的最佳实践
本文是Netty系列第7篇 上一篇文章我们深入学习了Netty逻辑架构中的核心组件EventLoop和EventLoopGroup,掌握了Netty的线程模型,并且介绍了Netty4线程模型中的无锁串 ...
- 五、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之MVVM中的EventAggregator
这一篇我们主要再看完示例12.13后,写了个例子,用于再Modules下执行ApplicationCommands,使用IActiveAware执行当前View的Commands,或者Applicat ...
- windows本地挂载HDFS
1.修改配置文件 进入配置文件目录: cd ${HADOOP_HOME}/etc/hadoop 修改core-site.xml: vim core-site.xml 在文件中增加以下内容: <p ...
- STM32启动文件详解及SystemInit函数分析(转)
;先在RAM中分配系统使用的栈,RAM的起始地址为0x2000_0000 ;然后在RAM中分配变量使用的堆 ;然后在CODE区(flash)分配中断向量表,flash的起始地址为0x0800_0000 ...
- miniFTP项目实战六
项目简介: 在Linux环境下用C语言开发的Vsftpd的简化版本,拥有部分Vsftpd功能和相同的FTP协议,系统的主要架构采用多进程模型,每当有一个新的客户连接到达,主进程就会派生出一个ftp服务 ...
- 数据结构与算法-排序(九)基数排序(Radix Sort)
摘要 基数排序是进行整数序列的排序,它是将整数从个位开始,直到最大数的最后一位截止,每一个进位(比如个位.十位.百位)的数进行排序比较. 每个进位做的排序比较是用计数排序的方式处理,所以基数排序离不开 ...
- NOIP 模拟 $20\; \rm 玩具$
题解 \(by\;zj\varphi\) 一道概率与期望好题 对于一棵树,去掉根后所有子树就是一个森林,同理,一个森林加一个根就是一棵树 设 \(f_{i,j}\) 为有 \(i\) 个点的树,高度为 ...
- redis如何实现分布式锁?
1.使用redis中的自增来实现 2.使用setnx + del # 如果不存在set(返回1),如果存在则失败(返回0) 为了避免死锁会加上一个过期时间 自增方式 boolean isSelf = ...