加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 什么是堆栈映射框架

发布时间:2020-12-15 07:36:22 所属栏目:Java 来源:网络整理
导读:我最近一直在关注 Java Virtual Machine Specifications(JVMS),试图更好地理解我的程序是如何工作的,但我找到了一个我不太了解的部分…… 第4.7.4节描述了StackMapTable属性,在该部分中,文档详细介绍了堆栈映射帧.问题在于它有点罗嗦,我通过榜样学得最好;不
我最近一直在关注 Java Virtual Machine Specifications(JVMS),试图更好地理解我的程序是如何工作的,但我找到了一个我不太了解的部分……

第4.7.4节描述了StackMapTable属性,在该部分中,文档详细介绍了堆栈映射帧.问题在于它有点罗嗦,我通过榜样学得最好;不是通过阅读.

我知道第一个堆栈映射帧是从方法描述符派生的,但我不明白如何(据说这是here解释的).另外,我并不完全理解堆栈映射帧的作用.我认为它们与Java中的块类似,但看起来好像你不能在彼此内部堆栈映射帧.

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

>堆栈映射框架有什么作用?
>如何创建第一个堆栈映射框架?

还有一个普遍的问题:

>有人能提供的解释比JVMS中给出的解释更少,更容易理解吗?

解决方法

Java要求验证所有加载的类,以便维护沙箱的安全性并确保代码可以安全地进行优化.请注意,这是在字节码级别完成的,因此验证不会验证Java语言的不变量,它只是根据字节码规则验证字节码是否有意义.

除此之外,字节码验证可确保指令格式正确,所有跳转都是方法中的有效指令,并且所有指令都对正确类型的值进行操作.最后一个是堆栈映射的来源.

问题是字节码本身不包含显式类型信息.通过数据流分析隐式确定类型.例如,iconst指令创建一个整数值.如果将其存储在插槽1中,则该插槽现在具有int.如果控制流从存储浮点的代码合并而来,则现在认为该槽具有无效类型,这意味着在覆盖它之前,您不能再使用该值执行任何操作.

从历史上看,字节码验证器使用这些数据流规则推断出所有类型.不幸的是,不可能通过字节码推断单个线性传递中的所有类型,因为向后跳转可能使已经推断的类型无效.经典验证程序通过迭代代码直到一切都停止变化来解决这个问题,可能需要多次传递.

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

简单地在代码中的每个单点存储每个值的类型显然会占用大量空间并且非常浪费.为了使元数据更小更高效,他们决定让它只列出跳转目标位置的类型.如果您考虑一下,这是您需要额外信息才能进行单程验证的唯一时间.在跳转目标之间,所有控制流都是线性的,因此您可以使用旧的推理规则推断位置之间的类型.

显式列出类型的每个位置称为堆栈映射帧. StackMapTable属性按顺序包含帧列表,但它们通常表示为与前一帧的差异,以减少数据大小.如果方法中没有帧,这在控制流永远不会加入时发生(即CFG是树),则可以完全省略StackMapTable属性.

因此,这是StackMapTable如何工作以及添加原因的基本思路.最后一个问题是如何创建隐式初始帧.答案当然是在方法的开头,操作数堆栈是空的,局部变量槽具有由方法参数的类型给出的类型,这些类型是从方法描述符确定的.

如果您习惯使用Java,那么方法参数类型在字节码级别的工作方式会有一些细微差别.首先,虚方法有一个隐含的第一个参数.其次,字节码级别不存在布尔,字节,字符和短路.相反,它们都是在幕后实现的.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读