细说虚拟机栈

引文

对JVM不了解的请先访问文章《Java虚拟机运行时数据区》,相信会让你大致了解各个内存区域的对应功能。接着,我们开始细说虚拟机栈     前文中说到:“虚拟机栈是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈,虚拟机栈表示Java方法执行的内存模型,每调用一个方法就会为每个方法生成一个栈帧(Stack Frame),用来存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法被调用和完成的过程,都对应一个栈帧从虚拟机栈上入栈和出栈的过程。虚拟机栈的生命周期和线程是相同的”。

虚拟机栈

其中,虚拟机栈是一个后入先出的栈。栈帧是保存在虚拟机栈中的,栈帧是用来存储数据和存储部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。线程运行过程中,只有一个栈帧是处于活跃状态,称为“当前活跃栈帧”,当前活动栈帧始终是虚拟机栈的栈顶元素。如下图所示: 

栈帧

上述内容已对栈帧做了大致介绍,接下去仔细描述栈帧中的操作数栈,动态连接,方法返回地址和一些额外的附加信息。 如下图所示:

1.局部变量表

局部变量表是一组局部变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java文件编译为Class文件时,就在方法表的Code属性的max_locals数据项中确定了该方法需要分配的最大局部变量表的容量。

2.操作数栈

操作数栈也常被称为操作栈,它是一个后入先出栈。JVM底层字节码指令集是基于栈类型的,所有的操作码都是对操作数栈上的数据进行操作,对于每一个方法的调用,JVM会建立一个操作数栈,以供计算使用。和局部变量一样。操作数栈的最大深度也是编译的时候写入到方法表的code属性的max_stacks数据项中。操作数栈的每一个元素可以是任意的Java数据类型,包括long、double。32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。栈容量的单位为“字宽”,对于32位虚拟机来说,一个“字宽”占4个字节,64位虚拟机来说,一个“字宽”占8个字节。当一个方法刚刚执行的时候,这个方法的操作数栈是空的,在方法执行的过程中,会有各种字节码指向操作数栈中写入和提取值,也就是入栈与出栈操作。例如,在做算术运算的时候就是通过操作数栈来进行的,又或者调用其它方法的时候是通过操作数栈来行参数传递的。 另外,在概念模型中,两个栈帧作为虚拟机栈的元素,相互之间是完全独立的,但是大多数虚拟机的实现里都会作一些优化处理,令两个栈帧出现一部分重叠。让下栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠在一起,这样在进行方法调用返回时就可以共用一部分数据,而无须进行额外的参数复制传递了。

3.动态连接

每个栈帧都包含一个指向运行时常量池中该栈帧所属性方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。在Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。另外一部分将在每一次的运行期期间转化为直接引用,这部分称为动态连接。

4.方法返回地址

当一个方法被执行后,有两种方式退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者(调用当前方法的的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法方式称为正常完成出口(Normal Method Invocation Completion)。另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用athrow字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的调用都产生任何返回值的。     无论采用何种方式退出,在方法退出之前,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。 方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用都栈帧的操作数栈中,调用PC计数器的值以指向方法调用指令后面的一条指令等。

5.附加信息

虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧中,例如与高度相关的信息,这部分信息完全取决于具体的虚拟机实现。在实际开发中,一般会把动态连接,方法返回地址与其它附加信息全部归为一类,称为栈帧信息。

时间: 2024-11-10 01:32:15

细说虚拟机栈的相关文章

JAVA虚拟机栈

JAVA虚拟机栈几个重要概念 java 虚拟机栈 = java virtual machine stacks = jvms 1.JVMS 线程私有,生命周期与线程相同 2.JVMS 描述的是JAVA方法执行的内存模型:JVMS在方法执行时会创建一个"栈帧" stack frame,用于记录局部变量表.操作数栈.动态链接.方法出口等,每一个方法从调用到完成的过程,就是一个栈帧在JVMS中入栈到出栈的过程 3.局部变量表存放基本数据类型.对象引用.returnAddress(字节码指令地址

Java虚拟机栈和本地方法栈

Java虚拟机栈的特征 线程私有 后进先出(LIFO)栈 存储栈帧,支持Java方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 Java本地方法栈的特征 线程私有 后进先出(LIFO)栈 作用是支撑Native方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 有一些虚拟机(如HotSpot)将Java虚拟机栈和本地方法栈合并实现 栈帧的概念和特征 Java虚拟机栈中存储的

Java虚拟机OOM之虚拟机栈和本地方法栈溢出(4)

一.在 Java 虚拟机规范中,对虚拟机栈这个区域规定了两种异常状况: (1)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常: (2)如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展,只不过 Java 虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError 异常. (3)与虚拟机栈一样,本地方法栈区域也会抛出 StackOverflowError 和OutOfMemoryEr

虚拟机栈和方法区的区别

虚拟机栈 虚拟机栈是线程私有的. 虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表.操作数栈.动态连接.方法出口等信息.每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程. 局部变量表存放了编译期可知的各种基本数据类型(boolean.byte.char.short.int.float.long.double).对象引用(reference类型.它不同于对象本身,可能是一个指向对象起始地址的引

java 虚拟机栈

与程序计数器一样,Java虚拟机栈也是线程私有的,他的生命周期与线程相同.虚拟机栈描述的是Java执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息.每个方法被调用直至执行完成的过程,就对应一个虚拟机栈由入栈到出栈的过程. 局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,long,double),对象引用(reference类型,他不同于对象本身,根据不同的虚拟机实现,它可能是一个指向

JVM【第六回】:【OutOfMemoryError异常之虚拟机栈和本地方法栈溢出】

由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此对于HotSpot来说,-Xoss参数(设置本地方法栈大小)虽然存在,但实际上是无效的,栈容量只由-Xss参数设定.关于虚拟机栈和本地方法栈,在Java规范中描述了两种异常: 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常. 在下面的代码中,如果将范围限制于单线程中的操作,尝试下面两种方法均无法让

Jvm(9),运行时数据---独占区---虚拟机栈

一,总览 Java Virtual Machine Stacks,线程私有,生命周期与线程相同,描述的是Java方法执行的内存模型:每一个方法执行的同时都会创建一个栈帧(Stack Frame),由于存储局部变量表.操作数栈.动态链接.方法出口等信息.每一个方法的执行就对应着栈帧在虚拟机栈中的入栈,出栈过程. 我们来看一个例子 public class Demo3 { public void test1() { System.out.println("我是test1的方法"); tes

探究Java虚拟机栈

前言 Java 虚拟机的内存模型分为两部分:一部分是线程共享的,包括 Java 堆和方法区:另一部分是线程私有的,包括虚拟机栈和本地方法栈,以及程序计数器这一小部分内存.今天我就 Java 虚拟机栈做一些比较浅的探究. 熟悉 Java 的同学应该都知道了,JVM 是基于栈的.但是这个"栈" 具体指的是什么?难道就是虚拟机栈?想要回答这个问题我们先要从虚拟机栈的结构谈起. 虚拟机栈 何为虚拟机栈虚拟机栈的栈元素是栈帧,当有一个方法被调用时,代表这个方法的栈帧入栈:当这个方法返回时,其栈帧

第二章:Java虚拟机栈和本地方法栈溢出

由于在Hotspot虚拟机中中不区分虚拟机栈和本地方法栈,因此通过-Xoss修改参数是无效的,可以通过修改-Xss设定. 如果线程请求的栈深度大于虚拟机允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError异常. 这两种异常有一些重叠的部分:当栈空间无法继续分配时,到底是内存太小,还是已经使用的栈空间过大,其本质只是对同一件事情的两种不同描述. 可以通过一下方法验证: 在使用-Xss参数减少栈内存容量