如果您最近一直在关注Java世界的消息,那么您可能听说过Oracle发布的最新Java 8构建,Java 8u11(和Java 7u65)引入了错误并破坏了一些流行的第三方工具,例如ZeroTurnaround的JRebel,Javassist,Google的Guice甚至Groovy本身。
JVM产生的错误是漫长而冗长的,但从本质上讲,它们看起来像这样:
Exception in thread "main" java.lang.VerifyError: Bad method call from inside of a branch
Exception Details:Location:com/takipi/tests/dc/DepthCounter.()V @10: invokespecial…
这些错误突然开始出现的原因是由于最新更新中的字节码验证程序比以前的版本更加严格。 与以前的版本不同,它不允许在分支代码中调用超级构造函数。
让我们分解一下。
Java字节码和字节码验证程序
字节码是JVM实际执行的中间语言,并且编写编译的.class文件。 JVM的机器代码(如果需要)。
通过Java,通过Scala,Groovy,Clojure等将所有基于JVM的语言编译成字节码。 JVM不知道也不在乎源语言是什么-它只知道字节码。
我不打算讨论字节码的工作原理 ,因为它是一个主题(或一个或多个帖子),但仅是要了解一下字节码的样子,以这种简单的Java方法为例:
int add(int x, int y) {int z = x + y;return z;
}
编译后,其字节码如下所示:
ILOAD x
ILOAD y
IADD
ISTORE z
ILOAD z
IRETURN
当JVM将类文件从类路径加载到内存中时,它首先必须确保字节码有效并且代码结构正确。 它基本上检查正在加载的代码是否可以实际执行。 如果字节码正确,则该类已成功加载到内存中; 否则,将引发VerifyError ,就像帖子开头的错误一样。
此过程称为字节码验证,而负责该过程的JVM部分是字节码验证程序。
为什么会破裂?
为了使字节码通过验证,它必须遵守类文件格式规范中定义的一组规则。 由于JVM最初是考虑到Java编程语言而设计的,因此许多规则都是直接从Java规则和约束派生的。
Java语言中这样一个众所周知的约束是,在执行其他任何操作之前,必须在构造函数中首先要做的是调用super(…)或this(…) 。 之前的任何一段代码-您的代码都不会编译。 即使您没有显式编写super() ,编译器也会在构造函数的最开始为您隐式地插入它。
字节码验证规则中至少在纸面上存在相同的约束。 但是,事实证明,直到这些最新的JDK更新,此约束才得以完全实施。 这意味着,尽管没有Java编译器会允许您编译此代码:
public static class ClassyClass {public ClassyClass() {if (checkSomething()) {super();} else {super(getSomething());}}
}
…等效的字节码将通过验证!
ALOAD thisINVOKESTATIC checkSomething() : booleanIFEQ L2INVOKESPECIAL super() : voidGOTO L2
L1: INVOKESTATIC getSomething() : intINVOKESPECIAL super(int) : void
L2: RETURN
您可以在上面的简化字节码中看到,在第一次调用超构造函数( INVOKESPECIAL )之前,既有一个调用( INVOKESTATIC ),也有一个分支( IFEQ —“如果相等”)。
请记住,尽管以上代码不是合法的Java,因此没有Java编译器会生成等效的字节码-还有许多其他可能的工具,例如不遵循Java约束的其他JVM语言的编译器,以及许多其他工具,例如字节码检测库。 在调用super之前执行代码的功能非常有用!
但是,Java 8 update 11带来了更严格的字节码验证程序,该验证程序拒绝在字节码中使用此类构造的类,并导致引发验证错误和JVM崩溃。
一方面,新的验证程序忠于该规范,从而确保我们的JVM免受不良代码的侵害。 另一方面,许多利用字节码检测的工具,例如调试器和Aspect Weavers(AOP),通常都使用上述结构。
怎么解决呢?
字节码验证程序的修复程序已提交 ,但尚未发布。 但是,许多受影响的工具和项目已经发布了固定版本和解决方法。
同时,如果您碰巧遇到这些错误之一,则可以尝试使用-noverify命令行参数启动JVM。 此选项指示JVM在加载类时跳过字节码验证。
翻译自: https://www.javacodegeeks.com/2014/08/oracles-latest-java-8-update-broke-your-tools-how-did-it-happen.html