堆栈帧

时间:2018-05-13 00:07:39

标签: java jvm

根据以下链接,java堆栈帧包含局部变量,操作数堆栈和当前类常量池引用。 http://blog.jamesdbloom.com/JVMInternals.html

同样来自Oracle“JVM结构”第2.6.3节。 “动态链接 - 每个帧(第2.6节)包含对运行时常量池(第2.5.5节)的引用,用于支持方法代码的动态链接的当前方法的类型。”

我还读过堆中的对象也有一个指向类数据的指针/引用。 https://www.artima.com/insidejvm/ed2/jvm6.html

堆栈帧将包含“当前类常量池引用”,并且它将引用堆中的对象,而堆中的对象又将指向类数据。这不是多余的吗?

例如。

public class Honda {
  public void run() {
    System.out.println("honda is running");
  } 
  public static void main(String[] args) {
  Honda h = new Honda();
  h.run(); //output honda is running
  }
}

当要执行h.run()时,jvm将创建一个新的堆栈帧并在堆栈帧上按h。 h将指向堆中的对象,而该对象又具有指向本田类数据的指针。堆栈帧也将具有当前类常量引用。它是否正确?如果没有,请详细说明。

3 个答案:

答案 0 :(得分:3)

  

这不是多余的吗?

对于实例方法和构造函数,可能它是多余的。

静态方法或类初始化伪方法并不是多余的。

(假设)冗余引用也可能被JIT编译器优化掉。 (或者它可能没有被优化掉......因为他们已经得出结论,冗余会导致平均执行速度更快。)或者可能是JVM 1 的实际实现与众不同。

请记住,JVM规范描述的是理想化的堆栈帧。实际的实现可能会有所不同......只要行为符合规范所说的方式。

关于@EJP关于规范性的观点,Java的唯一规范性引用是JLS和JVM规范,以及类库的Javadoc。您还可以查阅JVM本身的源代码。规范说明应该发生什么,代码(在某种意义上)说明了发生了什么。您可能在已发表的论文或网络文章中找到的文章不具有规范性,可能不正确或过时。

1 - 实际实施可能因版本而异,也可能因供应商而异。此外,我听说过一个JVM实现,其中字节码重写器在类加载时从标准字节码转换为另一种抽象机器语言。从性能角度来看,这不是一个好主意......但它肯定符合JVM规范的精神。

答案 1 :(得分:1)

  

堆栈帧将包含"当前类常量池引用"并且它将引用堆中的对象,而堆中的对象又指向类数据。这不是多余的吗?

你错过了那个陈述的先决条件,或者你错误引用了它,或者你看到它时显然是错误的。

"对堆中对象的引用"仅针对非静态方法添加,并且它引用隐藏的this参数。

如#34; Local Variables Array"

部分所述
  

局部变量数组包含在执行方法期间使用的所有变量,包括对this的引用,所有方法参数和其他本地定义的变量。对于类方法(即静态方法),方法参数从零开始,但是,例如方法为this 保留零时隙。

因此,对于静态方法,没有冗余。

this存在时,是否可以消除常量池引用?是的,但是那时需要一种不同的方法来定位常量池引用,需要不同的字节码指令,这样就会有不同的冗余。

始终在堆栈帧中的一个众所周知的位置提供常量池引用,简化了字节码逻辑。

答案 2 :(得分:0)

这里有两点。首先,有static个方法在没有this引用的情况下被调用。其次,对象实例的实际类不一定是我们实际执行其代码的方法的声明类。常量池引用的目的是使符号引用的解析和代码引用的常量的加载成为可能。在这两种情况下,我们都需要包含当前执行代码的类的常量池,即使该方法可能由this引用的实际类继承(如果private方法调用了this方法。另一个继承的方法,我们有一个用类static实例调用的方法,它甚至没有继承该方法。

甚至可能是当前执行的代码包含在接口中的情况,因此我们从不拥有它的实例,但仍然是具有常量池的类文件,在执行代码时必须可用。这不仅适用于Java 8及更新版本,它允许接口中的default<clinit>方法;早期版本也可能需要执行接口的static方法来初始化其this字段。

顺便说一句,即使在第一个局部变量中使用与this关联的对象引用调用实例方法,也不需要字节码指令将其保留在那里。如果不需要,可能会被任意值覆盖,将变量槽重用于其他目的。这并不排除后续指令需要常量池,如上所述,它不需要属于fabric-ca-server start -b admin:adminpw -u http://<enrollmentID>:<secret>@<parentserver>:<parentport> 的实际类。

当然,无论如何,该池引用是一个逻辑构造。当所有引用都已经被解析时,实现可以将代码转换为使用共享池或者根本不需要池。在内联之后,代码甚至可能不再具有专用的堆栈帧。