java.lang.VerifyError: (class: GregorSamsa, method: ...) Illegal target of jump or branch
java.lang.VerifyError: (class: GregorSamsa, method: ...) Illegal target of jump or branch
我在创建大量具有相当复杂的 XSLT 文档(2000 条件)的 xml 转换器 (javax.xml.transform.Transformer) 时遇到 VerifyError。请看例子:
public class XsltVerifyErrorTest {
private static final int MAX_ITERATIONS_COUNT = 1000000;
public static void main(String[] args) throws Exception {
final byte[] xslBytes = Files.readAllBytes(new File(args[0]).toPath());
for (int i = 0; i < MAX_ITERATIONS_COUNT; i++) {
System.out.println(String.format("Iteration %d", i));
final StreamSource xslSource = new StreamSource(new ByteArrayInputStream(xslBytes));
final Transformer transformer = TransformerFactory.newInstance().newTransformer(xslSource);
}
}
}
m49216@ubuntu:~/xslt-verify-error-test$ javac XsltVerifyErrorTest.java
m49216@ubuntu:~/xslt-verify-error-test$ java -showversion XsltVerifyErrorTest XsltVerifyErrorTest.xsl
openjdk version "1.8.0_162"
OpenJDK Runtime Environment (build 1.8.0_162-8u162-b12-0ubuntu0.16.04.2-b12)
OpenJDK 64-Bit Server VM (build 25.162-b12, mixed mode)
Iteration 0
Iteration 1
Iteration 2
...
Iteration 79
Iteration 80
Iteration 81
Exception in thread "main" java.lang.VerifyError: (class: GregorSamsa, method: TestTemplate signature: (Lcom/sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;ILjava/lang/Object;)V) Illegal target of jump or branch
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.newInstance(Class.java:412)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:455)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:762)
at XsltVerifyErrorTest.main(XsltVerifyErrorTest.java:25)
可以找到带有测试数据的完整示例here。
这个问题可以在 jdk 的许多版本上重现:8u101、8u121、8u152、8u161、8u162。
有人运行遇到过这个问题吗?它看起来像 jvm 错误吗?
编辑 1: JDK 9 也受到影响,但要重现该问题要困难得多 - 在我的机器上需要约 20 分钟和 660 次迭代。
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
Iteration 0
Iteration 1
Iteration 2
...
Iteration 658
Iteration 659
Iteration 660
Exception in thread "main" java.lang.VerifyError: (class: die/verwandlung/GregorSamsa, method: TestTemplate signature: (Lcom/sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;ILjava/lang/Object;)V) Illegal target of jump or branch
at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3110)
at java.base/java.lang.Class.getConstructor0(Class.java:3315)
at java.base/java.lang.Class.newInstance(Class.java:530)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:552)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:583)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:817)
at XsltVerifyErrorTest.main(XsltVerifyErrorTest.java:19)
编辑 2: 在 JDK 10 上工作正常。
我有机会更深入地研究问题并最终解决了这个难题。
这确实是 JDK 的 BCEL 库中的错误。
TransformerFactory
基于源 XSLT 生成字节码。字节码生成步骤之一是 remove temporary NOPs 并重新定位它们的目标(指向它们的分支指令)。
目标以 HashSet. And here is the problem. Instruction
class does not define hashCode
, and its equals
方法维护,对于分支完全错误:如果两个分支指令的目标相同,则认为它们相等。当然,这不是真的。
但是只要branch hashCode不同就不会出现这个问题。由于这些实际上是默认的身份哈希码,因此它们发生冲突的可能性很小。但是经过多次迭代,鉴于生成的方法非常大,最终发生了:两个具有相同目标的不同分支指令获得相同的标识hashCodes,并且由于冲突而覆盖了另一个。
运行带有-XX:hashCode=2
的程序(这会强制退化身份hashCodes),它会立即崩溃。
错误已在 BCEL-195 and integrated into JDK 10 in JDK-8163121 中修复。
早期的JDK版本仍然存在这个错误,但幸运的是你可以使用下面的解决方法。只需在应用程序启动时调用 Instruction.setComparator
:
import com.sun.org.apache.bcel.internal.generic.*;
...
Instruction.setComparator((i1, i2) -> {
if (i1 instanceof BranchInstruction) {
return i1 == i2;
}
return InstructionComparator.DEFAULT.equals(i1, i2);
});
我在创建大量具有相当复杂的 XSLT 文档(2000 条件)的 xml 转换器 (javax.xml.transform.Transformer) 时遇到 VerifyError。请看例子:
public class XsltVerifyErrorTest {
private static final int MAX_ITERATIONS_COUNT = 1000000;
public static void main(String[] args) throws Exception {
final byte[] xslBytes = Files.readAllBytes(new File(args[0]).toPath());
for (int i = 0; i < MAX_ITERATIONS_COUNT; i++) {
System.out.println(String.format("Iteration %d", i));
final StreamSource xslSource = new StreamSource(new ByteArrayInputStream(xslBytes));
final Transformer transformer = TransformerFactory.newInstance().newTransformer(xslSource);
}
}
}
m49216@ubuntu:~/xslt-verify-error-test$ javac XsltVerifyErrorTest.java
m49216@ubuntu:~/xslt-verify-error-test$ java -showversion XsltVerifyErrorTest XsltVerifyErrorTest.xsl
openjdk version "1.8.0_162"
OpenJDK Runtime Environment (build 1.8.0_162-8u162-b12-0ubuntu0.16.04.2-b12)
OpenJDK 64-Bit Server VM (build 25.162-b12, mixed mode)
Iteration 0
Iteration 1
Iteration 2
...
Iteration 79
Iteration 80
Iteration 81
Exception in thread "main" java.lang.VerifyError: (class: GregorSamsa, method: TestTemplate signature: (Lcom/sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;ILjava/lang/Object;)V) Illegal target of jump or branch
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.newInstance(Class.java:412)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:455)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:762)
at XsltVerifyErrorTest.main(XsltVerifyErrorTest.java:25)
可以找到带有测试数据的完整示例here。
这个问题可以在 jdk 的许多版本上重现:8u101、8u121、8u152、8u161、8u162。
有人运行遇到过这个问题吗?它看起来像 jvm 错误吗?
编辑 1: JDK 9 也受到影响,但要重现该问题要困难得多 - 在我的机器上需要约 20 分钟和 660 次迭代。
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
Iteration 0
Iteration 1
Iteration 2
...
Iteration 658
Iteration 659
Iteration 660
Exception in thread "main" java.lang.VerifyError: (class: die/verwandlung/GregorSamsa, method: TestTemplate signature: (Lcom/sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;ILjava/lang/Object;)V) Illegal target of jump or branch
at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3110)
at java.base/java.lang.Class.getConstructor0(Class.java:3315)
at java.base/java.lang.Class.newInstance(Class.java:530)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:552)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:583)
at java.xml/com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:817)
at XsltVerifyErrorTest.main(XsltVerifyErrorTest.java:19)
编辑 2: 在 JDK 10 上工作正常。
我有机会更深入地研究问题并最终解决了这个难题。
这确实是 JDK 的 BCEL 库中的错误。
TransformerFactory
基于源 XSLT 生成字节码。字节码生成步骤之一是 remove temporary NOPs 并重新定位它们的目标(指向它们的分支指令)。
目标以 HashSet. And here is the problem. Instruction
class does not define hashCode
, and its equals
方法维护,对于分支完全错误:如果两个分支指令的目标相同,则认为它们相等。当然,这不是真的。
但是只要branch hashCode不同就不会出现这个问题。由于这些实际上是默认的身份哈希码,因此它们发生冲突的可能性很小。但是经过多次迭代,鉴于生成的方法非常大,最终发生了:两个具有相同目标的不同分支指令获得相同的标识hashCodes,并且由于冲突而覆盖了另一个。
运行带有-XX:hashCode=2
的程序(这会强制退化身份hashCodes),它会立即崩溃。
错误已在 BCEL-195 and integrated into JDK 10 in JDK-8163121 中修复。
早期的JDK版本仍然存在这个错误,但幸运的是你可以使用下面的解决方法。只需在应用程序启动时调用 Instruction.setComparator
:
import com.sun.org.apache.bcel.internal.generic.*;
...
Instruction.setComparator((i1, i2) -> {
if (i1 instanceof BranchInstruction) {
return i1 == i2;
}
return InstructionComparator.DEFAULT.equals(i1, i2);
});