有堆栈地图框架的更好的解释吗?

我最近一直在研究Java虚拟机规范 ( Java Virtual Machine Specifications ,JVMS),以便更好地理解我的程序是如何工作的,但是我发现了一个我并没有得到的部分。

第4.7.4节描述了StackMapTable属性,在该节中,文档详细介绍了堆栈映射框架。 问题在于它有点罗嗦,我通过实例学习最好; 不通过阅读。

我明白,第一个堆栈映射框架是从方法描述符派生的,但是我不明白这是怎么解释的。另外,我并不完全理解堆栈映射框架在做什么。 我会假设它们与Java中的块相似,但看起来好像你不能在彼此内部有堆栈映射框架。

无论如何,我有两个具体的问题:

  • 堆栈地图框架是做什么的?
  • 第一个堆栈地图框架是如何创build的?

和一个一般的问题:

  • 有人可以提供一个比JVMS更less罗嗦,更容易理解的解释吗?

Java要求所有加载的类都要被validation,以保持沙盒的安全性,并确保代码的安全性能得到优化。 请注意,这是在字节码级别完成的,所以validation不validationJava 语言的不variables,它只是validation字节码是否符合字节码的规则。

除此之外,字节码validation确保指令格式正确,所有的跳转都是在方法内的有效指令,并且所有指令都以正确types的值进行操作。 最后一个是堆栈地图的来源。

关键是字节码本身不包含明确的types信息。 types通过数据stream分析隐式确定。 例如,一个图标指令创build一个整数值。 如果将其存储在插槽1中,则该插槽现在有一个int。 如果控制stream从存储float的代码合并而来,那么该槽现在被认为具有无效types,这意味着在覆盖它之前,您无法对该值做更多的操作。

历史上,字节码validation者使用这些数据stream规则推断所有types。 不幸的是,不可能通过字节码在单个线性传递中推断所有types,因为向后跳转可能使已经推断的types无效。 经典的validation者通过遍历代码解决了这个问题,直到所有东西都停止变化,可能需要多次传递。

但是,validation使Java的类加载速度变慢。 Oracle决定通过添加一个新的,更快的validation器来解决这个问题,这个validation器可以一次validation字节码。 要做到这一点,他们需要从Java 7开始的所有新类(Java 6处于过渡状态)携带有关其types的元数据,以便字节码可以一次性validation。 由于字节码格式本身不能改变,所以这个types信息被单独存储在一个名为StackMapTable的属性中。

简单地在代码中的每一个点上存储每一个值的types显然将占用大量的空间并且是非常浪费的。 为了使元数据变得更小更高效,他们决定让它只列出跳跃目标位置的types 。 如果你仔细想想,这是唯一一次你需要额外的信息做一次通过validation。 在跳转目标之间,所有的控制stream程都是线性的,所以你可以使用旧的推理规则推断两个位置之间的types。

明确列出types的每个位置称为堆栈映射框架。 StackMapTable属性按顺序包含一个帧列表,尽pipe它们通常表示为与前一帧不同以减小数据大小。 如果方法中没有框架,当控制stream程从不join时(即CFG是树),那么StackMapTable属性可以完全省略。

所以这是StackMapTable如何工作以及为什么添加的基本思路。 最后一个问题是如何创build隐式的初始框架。 答案当然是,在方法开始时,操作数栈是空的,局部variables槽具有由方法参数确定的types给出的types。

如果您习惯于Java,则在字节码级别上,方法参数types的工作方式会有一些细微的差别。 首先,虚拟方法有一个隐含的this作为第一个参数。 其次,在字节码级别不存在booleanbytecharshort 。 相反,它们都是在幕后实施的。