jvm中栈的frames详解

flydean flydean     2022-12-06     592

关键词:

简介

我们知道JVM运行时数据区域专门有一个叫做Stack Area的区域,专门用来负责线程的执行调用。那么JVM中的栈到底是怎么工作的呢?快来一起看看吧。

JVM中的栈

小师妹:F师兄,JVM为每个线程的运行都分配了一个栈,这个栈到底是怎么工作的呢?

小师妹,我们先看下JVM的整体运行架构图:

技术图片

我们可以看到运行时数据区域分为5大部分。

堆区是存储共享对象的地方,而栈区是存储线程私有对象的地方。

因为是栈的结构,所以这个区域总是LIFO(Last in first out)。我们考虑一个方法的执行,当方法执行的时候,就会在Stack Area中创建一个block,这个block中持有对本地对象和其他对象的引用。一旦方法执行完毕,则这个block就会出栈,供其他方法访问。

Frame

JVM中的stack area是由一个个的Frame组成的。

Frame主要用来存储数据和部分结果,以及执行动态链接,方法的返回值和调度异常。

每次调用方法时都会创建一个新Frame。当Frame的方法调用完成时,无论该方法是正常结束还是异常结束(它引发未捕获的异常),这个frame都会被销毁。

Frame是从JVM中的stack area中分配的。

每个frame都由三部分组成,分别是自己的local variables数组,自己的operand stack,以及对当前方法的run-time constant pool的引用。

在线程的执行过程中,任何一个时刻都只有一个frame处于活动状态。这个frame被称为current frame,它的方法被称为current 方法,定义当前方法的类是当前类。

如果frame中的方法调用另一个方法或该frame的方法结束,那么这个frame将不再是current frame。

每次调用新的方法,都会创建一个新的frame,并将控制权转移到调用新的方法生成的框架。

在方法返回时,当前frame将其方法调用的结果(如果有的话)传回上一个frame,并结束当前frame。

请注意,由线程创建的frame只能有该线程访问,并且不能被任何其他线程引用。

技术图片

Local Variables本地变量

每个frame都包含一个称为其本地局部变量的变量数组。frame的局部变量数组的长度是在编译的时候确定的。

单个局部变量可以保存以下类型的值:boolean, byte, char, short, int, float, reference, 或者 returnAddress。

如果对于long或double类型的值需要使用一对局部变量来存储。

局部变量因为存储在数组中,所以直接通过数字的索引来定位和访问。

注意,这个数组的索引值是从0开始,到数组长度-1结束。

单个局部变量直接通过索引来访问就够了,那么对于占用两个连续局部变量的long或者double类型来说,怎么访问呢?

比如说一个long类型占用数组中的n和n+1两个变量,那么我们可以通过索引n值来访问这个long类型,而不是通过n+1来访问。

注意,在JVM中,并不一定要求这个n是偶数。

那么这些局部变量有什么用呢?

Java虚拟机使用局部变量在方法调用时传递参数。

我们知道在java中有两种方法,一种是类方法,一种是实例方法。

在类方法调用中,所有参数都从局部变量0开始在连续的局部变量中传递。

在实例方法调用中,局部变量0始终指向的是该实例对象,也就是this。也就是说真实的参数是从局部变量1开始存储的。

Operand Stacks

在每个frame内部,又包含了一个LIFO的栈,这个栈叫做Operand Stack。

刚开始创建的时候,这个Operand Stack是空的。然后JVM将local variables中的常量或者值加载到Operand Stack中去。

然后Java虚拟机指令从操作数堆栈中获取操作数,对其进行操作,然后将结果压回操作数堆栈。

比如说,现在的Operand Stack中已经有两个值,1和2。

这个时候JVM要执行一个iadd指令,将1和2相加。那么就会先将stack中的1和2两个数取出,相加后,将结果3再压入stack。

最终stack中保存的是iadd的结果3。

注意,在Local Variables本地变量中我们提到,如果是long或者double类型的话,需要两个本地变量来存储。而在Operand Stack中,一个值可以表示任何Java虚拟机类型的值。也就是说long和double在Operand Stack中,使用一个值就可以表示了。

Operand Stack中的任何操作都必须要确保其类型匹配。像之前提到的iadd指令是对两个int进行相加,如果这个时候你的Operand Stacks中存储的是long值,那么iadd指令是会失败的。

在任何时间点,操作数堆栈都具有关联的深度,其中long或double类型的值对该深度贡献两个单位,而任何其他类型的值则贡献一个单位深度。

Dynamic Linking动态链接

什么是动态链接呢?

我们知道在class文件中除了包含类的版本、字段、方法、接口
等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。

所谓字面量就是常说的常量,可以有三种方式,分别是:文本字符串,八种基本类型和final类型的常量。

而符号引用是指用符号来描述所引用的目标。

符号引用和直接引用有什么区别呢? 我们举个例子。

比如我们定义了String name="jack", 其中jack是一个字面量,会在字符串常量池(String Pool)中保存一份。

如果我们存储的时候,存的是name,那么这个就是符号引用。

如果我们存储的是jack在字符串常量池中地址,那么这个就是直接引用。

从上面的介绍我们可以知道,为了实现最终的程序正常运行,所有的符号引用都需要转换成为直接引用才能正常执行。

而这个转换的过程,就叫做动态链接。

动态链接将这些符号方法引用转换为具体的方法引用,根据需要加载类以解析尚未定义的符号,并将变量访问转换为与这些变量的运行时位置关联的存储结构中的适当偏移量。

方法执行完毕

方法执行完毕有两种形式,一种是正常执行完毕,一种是执行过程中抛出了异常。

正常执行完毕的方法可以值返回给调用方。

这种情况下frame的作用就是恢复调用程序的状态,包括其局部变量和操作数堆栈,并适当增加调用程序的程序计数器以跳过方法调用指令。

如果方法中抛出了异常,那么该方法将不会有值返回给调用方。

本文已收录于:http://www.flydean.com/jvm-thread-stack-frames/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

技术图片


VC++中栈的大小是多少?

】VC++中栈的大小是多少?【英文标题】:whatisthesizeofstackinVC++?【发布时间】:2012-12-0917:01:57【问题描述】:我想要有关VC++(32位和64位)中堆栈溢出的准确信息,尤其是递归。在调试模式下,这种情况在递归中很快发生(就像450... 查看详情

tarjan中栈的分析与slt栈的实现

首先看一下手写的栈:1do{2printf("%d",stack[index]);3visit[stack[index]]=0;4index--;5}while(x!=stack[index+1]);//出栈,并且输出。6printf(" ");我们可以发现。x是与index的上一个元素比较的 举个例子栈:13245  x=2这样的话会输出5 4  查看详情

python中栈的实现(代码片段)

栈是一种线性数据结构,用先进后出或者是后进先出的方式存储数据,栈中数据的插入删除操作都是在栈顶端进行,常见栈的函数操作包括empty() –返回栈是否为空–TimeComplexity:O(1)size() – 返回栈的长度–TimeComplexity:O... 查看详情

java中栈的两种实现

栈是先进后出的数据结构,主要有弹栈,入栈两种操作。数组版1packagestack;23/***4*栈的数组实现--java5*栈是先进后出的数据结构,主要有弹栈,入栈两种操作6*由于该栈是由数组实现的,数组的长度是固定的,当栈空间不足时,7*... 查看详情

java中栈的应用,括号匹配(代码片段)

1packageedu.yuliang.Data_Structure_Basics;23importorg.omg.PortableInterceptor.SYSTEM_EXCEPTION;4/*5给定一个只包括 ‘(‘,‘)‘,‘‘,‘‘,‘[‘,‘]‘ 的字符串,判断字符串是否有效。67有效字符串需满足:89左括号必须用相同类型的右... 查看详情

jvm整体结构-java栈详解(代码片段)

...行时数据区(内存结构)java栈java栈结构图java栈中栈帧运行过程堆执行引擎注JVM整体结构JVM与VMWare都是虚拟机的一种。JVM由三个子系统构成:类加载器子系统运行时数据区(内存结构)执行引擎(包含垃圾... 查看详情

java中栈的使用

和C++里面一样,有入栈,弹栈,查找函数importjava.util.*;(引入包含栈类的头文件)相关函数介绍booleanempty()测试堆栈是否为空。Epeek()查看堆栈顶部的对象,但不从堆栈中移除它。Epop()移除堆栈顶部的对象,并作为此函数的值返回该... 查看详情

1289大鱼吃小鱼(stl中栈的应用)(代码片段)

》》点击进入测试《《有N条鱼每条鱼的位置及大小均不同,他们沿着X轴游动,有的向左,有的向右。游动的速度是一样的,两条鱼相遇大鱼会吃掉小鱼。从左到右给出每条鱼的大小和游动的方向(0表示向左,1表示向右)。问... 查看详情

java中栈的创建与其常见的应用场景(代码片段)

...但是它在元素的插入和删除操作方面比较灵活。(1)Java中栈的创建方式  ①使用Stack类  Java提供了最容易根据名字想起的Stack类,这也是在Java6以及更早版本常用的方式。Stack<String>stack=newStack<>();//创建一个栈,泛... 查看详情

stack栈

...。因此实现非常的方便。下面就给出栈的函数列表和VS2008中栈的源代码,在STL中栈一共就 查看详情

jvm运行时内存空间详解——虚拟机栈(代码片段)

文章目录1.什么是虚拟机栈2.什么是栈帧3.设置虚拟机栈的大小4.局部变量表5.操作数栈6.动态链接7.方法返回地址通过上一篇文章,我们大体了解了JVM的整体架构,其分为:元数据(JDK7是方法区)、堆、虚拟机... 查看详情

jvm中堆和栈的区别

...空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。5、栈的内存要远远小于堆内存。 查看详情

jvm(30),理解升级----java中堆内存和栈内存详解

java中内存分配策略及堆和栈的比较   1内存分配策略  按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因... 查看详情

jvm堆和栈的区别

 栈内存: 程序在栈内存中运行 栈中存的是基本数据类型和堆中对象的引用 栈是运行时的单元 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据 一个线程一个独立的线程栈  堆内存:... 查看详情

jvm虚拟机栈5栈的面试题

...    举例栈溢出的情况?(StackOverflow):固定大小栈的栈帧爆掉      通过-Xss设置栈的大小:OOM ,可变大小栈扩容时,没有更多的内存可供扩容     调整栈大小,就能保证不溢出吗?      不能... 查看详情

[转]jvm堆和栈的区别

物理地址堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩)栈使... 查看详情

jvm堆和栈的区别

...成的对象,JVM会抛出java.lang.OutOfMemoryError。 空间大小栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满。如果递归没有及时跳出,很可能发生StackOverFlowError问题。你可以通过-Xss选项设置栈内存的大... 查看详情

堆和栈的区别详解

...释放,不会造成内存泄露,不得用delete或free操作,因为栈的空间小所以在栈 查看详情