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. 第八周PSP(11.5--11.9)

    2016.11.5 2016.11.6 2016.11.7 2016.11.8 2016.11.9

  2. node解决跨域问题

    app.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin", "*& ...

  3. MySQL中EXPLAIN解释命令 查看索引是否生效

    explain显示了mysql如何使用索引来处理select语句以及连接表.可以帮助选择更好的索引和写出更优化的查询语句. 使用方法,在select语句前加上explain就可以了: 如: expla ...

  4. TClientDataSet[9]: 计算字段和 State

    TClientDataSet 中计算字段有两种: Calculated(计算字段).InternalCalc(内部计算字段). 两者区别是: Calculated 在每次需要时都要重新计算; Inte ...

  5. WP-PostViews使用

    1.在后台安装次插件 2.获取多少天之内的访问排名最高的记录 2.1 添加相应方法代码到wp-postviews.php文件中,据体代码可以网上找(本人自己可以在自己本机的例子查看到),这里只是记录大 ...

  6. 【设计模式】—— 观察者模式Observer

    前言:[模式总览]——————————by xingoo 模式意图 观察者模式,也叫发布/订阅模式,从名字来说就能察觉到它的过程应该是,发布——其他人接受. 这个模式定义了对象之间的一种依赖关系,当一 ...

  7. 【设计模式】—— 装饰模式Decorator

    前言:[模式总览]——————————by xingoo 模式意图 在不改变原来类的情况下,进行扩展. 动态的给对象增加一个业务功能,就功能来说,比生成子类更方便. 应用场景 1 在不生成子类的情况下 ...

  8. POJ 2251 Dungeon Master /UVA 532 Dungeon Master / ZOJ 1940 Dungeon Master(广度优先搜索)

    POJ 2251 Dungeon Master /UVA 532 Dungeon Master / ZOJ 1940 Dungeon Master(广度优先搜索) Description You ar ...

  9. Oracle 11g DRCP连接方式——基本原理

    学习Oracle是一个复杂.繁琐的过程.在浩如烟海的Oracle官方资料.新特性.MOS资料和各种Internal知识面前,我们总是觉得力不从心.不知所措.但是,这往往也就是我们不断坚持.积累和追寻的 ...

  10. 伤不起:File.toPath() & Paths.get()

    java.nio.file.Path这个类应该是从java7才开始有的. 通过File类有两个方法可以转换成Path. 1. Path p = Paths.get(file.toURI());  // ...