2.6. 栈帧

  栈帧用于存储数据和部分结果,同样也用于执行动态链接,返回方法的值和分派异常。

  当方法被调用的时候会创建一个新的栈帧。当一个方法调用结束时,它对应的栈帧就被销毁了,不管是正常调用结束还是意外结束(抛出了未被捕获的异常)。栈帧分配在线程创建的虚拟机栈中。每个栈帧都有自己的局部变量表,操作数栈,以及当前方法的类的运行时常量池的引用。

  可以使用附加的特定于实现的信息来扩展帧,例如调试信息

  局部变量表和操作数栈在编译时期就确定了,并且通过栈帧关联的方法的code提供。因此栈帧的大小仅仅取决于虚拟机的实现,和方法调用时这些结构所分配的内存。

  在一个给定的线程上,任何时刻只有正在执行的方法的栈帧时活动的。这个帧被称之为当前帧,它的方法就是当前方法,对应的类就是当前类。局部变量和操作数堆栈的操作通常是指当前帧上的操作。

  一个帧的方法调用另外一个方法,或者这个帧的方法结束了,那么这个帧不再是当前帧了。当一个方法被调用,一个新的栈帧就会被创建,当控制权转移到这个新方法时,这个帧就变成当前帧。在方法返回时,当前帧将其方法调用的结果(如果有)传递回前一帧,然后当前一帧成为当前帧时丢弃当前帧。

  注意线程创建的栈帧是属于这个线程的,不可能被别的线程引用。

2.6.1 局部变量表

  每个栈帧都包含一组变量,称之为它的局部变量表。帧的局部变量数组的长度在编译时确定,并以类或接口的二进制表示形式提供,存储在帧相关的方法的code属性中。

  一个局部变量可以保存boolean,byte,char,short,int,float,reference或者returnAddress的值。一对局部变量可以保存long或者double的值。

  局部变量表根据索引定位。第一个局部变量的索引是0。如果一个整数大于等于0并且小于局部变量表的长度,就是这个局部变量表的索引值。

  long或者double的值占据两个连续的局部变量,这样的值只用两个变量中最小的索引值来定位。例如,double的值存在局部变量表的索引n处,那么实际是它也占据了n+1的局部变量位置,但是无法根据n+1的索引来加载这个变量。索引值为n+1的变量可以被写入,但是,这样做会使局部变量n的内容无效。

  上文中提及的局部变量n的n值并不要求一定是偶数,Java虚拟机也不要求double和long类型数据采用 64 位对齐的方式存放在连续的局部变量中。虚拟机实现者可以自由地选择适当的方式,通过两个局部变量来存储一个 double 或 long 类型的值。

  java虚拟机使用局部变量表在方法调用时传递参数。当类的方法被调用时,所有参数都被传递到索引从0开始的连续的局部变量表中。当一个实例的方法调用时,索引为0的局部变量总是被用来传递这个实例对象的引用(java编程语言中this)。后续其他参数将会传递到从1开始的连续的局部变量表中。

2.6.2 操作数栈

  每个栈帧都有一个后进先出(LIFO)的栈,即操作数栈。一个栈帧的操作数栈的最大深度在编译器确定,由这个帧所在的方法的code属性提供。

  当上下文清晰的适合,我们有时会把当前帧的操作数栈简称操作数栈。

  栈帧刚创建时,他的操作数栈时空的。java虚拟机提供一系列指令从局部变量表或者字段中去加载常量或者变量到操作数栈中。其他的一些虚拟机指令从操作数栈中取出操作数,对它们进行操作,然后将结果推入到操作数栈中。操作数栈也被用户准备参数参数传递给方法和接受方法的结果。

  例如,iadd指令用户将两个int数相加。它需要操作数栈顶部的两个int数字进行相加,这两个数字需要被之前的指令提前入栈。两个int值从操作数栈中出栈,然后它们相加,将他们之和入栈到操作数栈。子计算可以操作数栈中嵌套进行,它们的结果能够被外围的计算使用。

  每个入栈的成员都能保存任何一个虚拟机类型,包括long类型和double类型。

  必须使用符合它们类型的操作去操作操作数栈中的值。例如,不可能入栈两个int值,然后将它们当成long类型来操作,或者入栈两个两个float值,接着使用iadd命令来操作。只有一小部分虚拟机指令(如dub和swap指令)操作运行时数据区域时,将其中的值当作原始值,而不用考虑它们的特殊类型;这些指令不能用于修改或者分解单个值。这些操作数栈上面的操作限制时用class文件验证来强制保证的。

  任何时刻,一个操作数栈都有确定的深度,long或者double占用两个单元的深度,其他类型只会占用一个类型的深度。

2.6.3 动态链接

  每一个帧都包含一个运行时常量池的引用,用于当前方法支持方法代码的动态链接(dynamic linking)。一个方法在class文件中的code,描述一个方法调用和变量访问时通过符号引用(symbolic references)。动态链接将这些方法的符号引用转换为具体的方法引用,根据需要加载类以解析尚未定义的符号,并将变量访问转换为与这些变量的运行时位置相关的存储结构中的适当偏移。

  方法和变量的这种后期绑定使得,当前方法使用了其他类中的方法或者变量发生变化时,不太可能破坏当前方法的代码。

2.6.4 方法的正常调用完成

  一个方法调用正常完成是指,这个调用没有导致异常出现,既不是java虚拟机直接抛出也不是执行了一个具体的throw语句。如果当前方法的调用正常完成,然后可能返回一个值给调用者。当调用的方法执行了某一种return指令,才会出现返回值,return指令必须选择符合返回值类型的指令。

  在这种情况下,当前帧的目的是用于恢复调用者的状态,包括其局部变量和操作数堆栈;然后调用者的程序计数器适当地递增以跳过方法调用指令。然后在调用者的方法的帧中继续正常执行,返回值(如果有)被推送到该帧的操作数堆栈。

2.6.5 方法的异常调用完成

  一个方法异常调用结束是指,jiava虚拟机指令执行该方法时导致了虚拟机抛出一个异常,并且这个异常没有在该方法中处理。执行athrow指令同样会导致一个异常被显示的抛出,如果当前方法没有捕获这个异常,该方法调用会异常完成。方法调用异常结束不会返回值给它的调用者。

java虚拟机规范(se8)——java虚拟机结构(三)的更多相关文章

  1. java虚拟机规范(se8)——java虚拟机结构(一)

    本文翻译自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 第二章 虚拟机结构 本文档描述了一个抽象的虚拟机规范,并不描述 ...

  2. java虚拟机规范(se8)——java虚拟机结构(二)

    2.5 运行时数据区域 java虚拟机定义了多个用于程序执行期间的运行时数据区域.这些数据区域中一些随着java虚拟机的启动而创建,随着虚拟机的退出而销毁.其他的数据区域时和线程相关的.线程相关数据区 ...

  3. java虚拟机规范(se8)——java虚拟机结构(六)

    2.11 指令集简介 java虚拟机指令由一个字节的操作码,接着时0个或多个操作数组成,操作码描述了执行的操作,操作数提供了操作所需的参数或者数据.许多指令没有操作数只包含一个操作码. 如果忽略异常处 ...

  4. java虚拟机规范(se8)——java虚拟机的编译(一)

    本文翻译自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 第三章 java虚拟机的编译 java虚拟机是设计用来支持ja ...

  5. java虚拟机规范(se8)——java虚拟机的编译(四)

    3.12 抛出和处理异常 在程序中使用throw关键字来抛出异常.编译结果很简单. void cantBeZero(int i) throws TestExc { if (i == 0) { thro ...

  6. java虚拟机规范(se8)——java虚拟机结构(五)

    2.10 异常 java虚拟机中的异常用Throwable类或者它的子类的实例来表示.抛出一个异常会导致立即非本地(an inmediate nolocal)的控制转移,从发生异常的地方跳到处理异常的 ...

  7. java虚拟机规范(se8)——java虚拟机结构(四)

    2.7 对象的表示 java虚拟机并不要求对象满足任何特定的内部结构. 在Oracle的一些Java虚拟机实现中,对类实例的引用是指向句柄的指针,该句柄本身是一对指针:一个指向包含对象方法的表和指向表 ...

  8. java虚拟机规范(se8)——java虚拟机的编译(三)

    3.6 接受参数 如果n个参数传给一个实例的方法,按照约定,它们被接受并放在这个新方法创建的栈帧中的局部变量表里,在局部变量表中的序号从1到n.这些参数按照它们传递过来的顺序存放.例如: int ad ...

  9. java虚拟机规范(se8)——java虚拟机的编译(二)

    3.3 算术运算 java虚拟机通常在操作数栈上进行算术运算(例外情况是iinc指令,它直接增加一个局部变量的值).例如下面的align2grain()方法,它的作用是将int值对齐到2的指定次幂: ...

随机推荐

  1. Docker(十七)-修改Docker容器启动配置参数

    有时候,我们创建容器时忘了添加参数 --restart=always ,当 Docker 重启时,容器未能自动启动, 现在要添加该参数怎么办呢,方法有二: 1.Docker 命令修改 docker c ...

  2. [转帖] Linux 下面栈空间大小的实验

    比如局部变量是保存在栈空间中的,今天突然在想栈的上限是多大呢,什么时候才会栈溢出? ulimit 命令 linux下使用ulimit 命令可以查看系统的很多上限值. ulimit -a 查看所有 ul ...

  3. 浅谈final修饰的变量

    一直大概的知道final关键字的作用,但是自己实际工作中却很少用,除非在声明一些常量值的时候,今天忽然自己在项目中用一个map进行存储一些值.一开始我只是用private修饰的,心里想的是如果fina ...

  4. The writing on the wall 南京网络赛2018B题

    样例输入复制 2 3 3 0 3 3 1 2 2 样例输出复制 Case #1: 36 Case #2: 20 题目来源 ACM-ICPC 2018 南京赛区网络预赛 题意: 就是求图中去掉涂黑的方格 ...

  5. 【刷题】BZOJ 4977 [Lydsy1708月赛]跳伞求生

    Description 小Q最近沉迷于<跳伞求生>游戏.他组建了一支由n名玩家(包括他自己)组成的战队,编号依次为1到n.这个游 戏中,每局游戏开始时,所有玩家都会从飞机上跳伞,选择一个目 ...

  6. 谈谈Java引用和Threadlocal的那些事

    1 背景 某一天在某一个群里面的某个群友突然提出了一个问题:"threadlocal的key是虚引用,那么在threadlocal.get()的时候,发生GC之后,key是否是null?&q ...

  7. BZOJ 3876 支线剧情 | 有下界费用流

    BZOJ 3876 支线剧情 | 有下界费用流 题意 这题题面搞得我看了半天没看懂--是这样的,原题中的"剧情"指的是边,"剧情点"指的才是点. 题面翻译过来大 ...

  8. 动态生成web表-asp.net table

    1. 页面上定义一个server 的table <table style="width: 100%" id="tbContent" runat=" ...

  9. Linux上设置开机启动Java程序

    在Linux上设置开机启动Java程序,例如:test.jar 在Linux上启动Java程序的命令: nohup java -jar test.jar >/dev/>& & ...

  10. python deamon(守护)线程的作用

    stackoverflow 上的解释 某些线程执行后台任务,例如发送keepalive数据包,或执行定期垃圾收集,或任何.这些仅在主程序运行时有用,并且一旦其他非守护程序线程退出就可以将其关闭. 没有 ...