【问题标题】:How safe is it to use -XX:-UseSplitVerifier?使用 -XX:-UseSplitVerifier 有多安全?
【发布时间】:2013-02-21 14:06:30
【问题描述】:

使用检测的 JDK7 编译代码存在已知的兼容性问题。 至于http://www.oracle.com/technetwork/java/javase/compatibility-417013.html

版本号为 51 的类文件是使用类型检查验证器专门验证的,因此方法必须在适当的时候具有 StackMapTable 属性。对于版本 50 的类文件,如果文件中的堆栈映射丢失或不正确,Hotspot JVM 将(并继续)故障转移到类型推断验证器。对于版本 51(Java SE 7 的默认版本)的类文件,不会发生此故障转移行为。 任何修改版本 51 类文件中字节码的工具都必须确保更新堆栈图信息以与字节码保持一致才能通过验证。

解决方案是使用-XX:-UseSplitVerifier,总结如下: https://community.oracle.com/blogs/fabriziogiudici/2012/05/07/understanding-subtle-new-behaviours-jdk-7

它有多安全?我想甲骨文把这个检查放在里面是有原因的。如果我不使用它,我可能会冒其他问题的风险。

使用-XX:-UseSplitVerifier会有什么后果?

谢谢,

彼得。

【问题讨论】:

  • 作为对未来读者的扩展和可能的后果,但在 Java8 中,该标志已被弃用。阅读this了解详情。

标签: java java-7


【解决方案1】:

简而言之,它非常安全。

从 Java 6 开始,Oracle 的编译器使用 StackMapTable 制作类文件。基本思想是编译器可以显式指定对象的类型,而不是让运行时来做。这在运行时提供了微小的加速,以换取编译期间的一些额外时间和编译的类文件(前面提到的 StackMapTable)的一些复杂性。

作为一项实验性功能,它在 Java 6 编译器中默认未启用。如果不存在 StackMapTable,则运行时默认验证对象类型本身。

直到 Java 7。Oracle 强制要求:编译器生成它们,运行时验证它们。如果 StackMapTable 不存在,它仍然使用旧的验证器......但仅限于 Java 6 或更早版本(版本 50)的类文件。使用 StackMapTable 需要 Java 7 类文件(版本 51),因此运行时不会对它们进行同样的处理。

如果您的类文件是在没有 StackMapTable 的情况下生成的,那么这只是一个问题。例如,如果您使用的是非 Oracle JVM。或者,如果你后来弄乱了字节码——比如将其与调试器、优化器或代码覆盖分析器一起使用。

但你可以绕过它! Oracle 的 JVM 提供了 -XX:+UseSplitVerifier 来强制运行时回退到旧的类型验证器。它不关心 StackMapTable。

在实践中,所希望的运行速度和效率优化尚未实现:如果存在,还不足以让任何人注意到。由于新类型验证器不提供任何新功能(只是优化),因此将其关闭是非常安全的。

如果您搜索 JSR 202,Oracle 的解释位于 http://www.oracle.com/technetwork/java/javase/compatibility-417013.html

【讨论】:

  • 是的,StackMapTable 对 Sun 来说完全是一件糟糕的工作。除此之外,如果他们只是尝试(或问我),他们本可以大大提高旧验证器的速度。
  • 轻微的挑剔,它是-XX:-UseSplitVerifier 而不是-XX:+UseSplitVerifier。但我想现在已经无关紧要了,因为它已经在 J​​ava 8 中被删除了。
【解决方案2】:

是的——它是安全的。正如 Judebert 所说,它只是稍微减慢了类加载速度。

添加更多信息:StackMap 表到底是什么?好吧,字节码验证器需要对类文件中的代码进行两次传递,以验证正在传递和使用的数据类型是否正确。第一遍是较慢的一遍,它对所有代码的分支进行流分析,以查看每个字节码指令的堆栈上可能有什么类型的数据。第二遍查看每条指令,看它是否可以有效地对所有这些类型进行操作。

这是关键:编译器已经掌握了第一遍生成的所有信息 - 因此(在 Java 6 和 7 中)它将其存储在类文件的 StackMap 表中。

这加快了类加载,因为类加载器不必执行第一次传递。这就是它被称为拆分验证器的原因,因为工作在编译器和运行时加载机制之间进行了拆分。当您使用 -XX:-UseSplitVerifier 选项时,您告诉 Java 在类加载时执行 both 传递(并忽略任何 StackMap 表)。许多产品(比如在加载时修改字节码的分析器)最初并不知道 StackMap 表,所以当他们在加载时修改类时,编译器的 StackMap 表已经过时并导致错误。

总之,-XX:-UseSplitVerifier 选项会减慢类加载速度。它不会影响安全性、运行时性能或功能。

【讨论】:

  • +1 用于解释 StackMap Table 的根据。在 StackMap 表的所有谷歌搜索页面中,这是我发现的最简单、准确和清晰的解释。
  • 不仅仅是验证者必须做两遍。当涉及到(可能是嵌套的)循环和/或(可能是嵌套的)异常处理程序时,第一步可能意味着对相同代码的任意数量的传递,因为相同的代码可能会在不同的先决条件下执行,并且验证者必须找出什么是常见的。这也可能意味着在类和接口层次结构中搜索两个或多个类型的最具体的常见基类型。
【解决方案3】:

堆栈地图框架是在 Java 7 中添加的,“prashant”认为这个想法是有缺陷的,并建议开发人员始终使用-XX:-UseSplitVerifier 标志来避免使用它们。

阅读更多:Java 7 Bytecode Verifier: Huge backward step for the JVM

【讨论】:

  • 那么这是否意味着验证器没有在 Java 7 中运行?还是只执行其中的一部分,而其他部分(如文章所说)现在应该由编译器管理?
  • @DinisCruz 验证程序始终运行。使用 -XX:-UseSplitVerifier 告诉 jvm 使用 2 pass 方法,这比新的默认值稍慢——它只运行引用 StackMap Table 数据的第二个 pass。换句话说,第一遍总是由编译器运行——或者运行时再次执行第一遍,或者它使用编译器存储在 StackMap 表中的中间结果。
猜你喜欢
  • 2019-05-23
  • 2011-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-17
  • 2014-02-28
  • 1970-01-01
  • 2012-09-30
相关资源
最近更新 更多