我认为您确实在查看 OSR 中的 JVM 错误。使用来自@Gray 的简化程序(稍作修改以打印错误消息)和一些选项来混淆/打印 JIT 编译,您可以看到 JIT 发生了什么。而且,您可以使用一些选项将其控制到可以抑制问题的程度,这为这是一个 JVM 错误提供了很多证据。
运行为:
java -XX:+PrintCompilation -XX:CompileThreshold=10000 phil.StrangeRaceConditionTest
你会得到一个错误情况(像其他人一样,大约 80% 的运行)并且编译打印有点像:
68 1 java.lang.String::hashCode (64 bytes)
97 2 sun.nio.cs.UTF_8$Decoder::decodeArrayLoop (553 bytes)
104 3 java.math.BigInteger::mulAdd (81 bytes)
106 4 java.math.BigInteger::multiplyToLen (219 bytes)
111 5 java.math.BigInteger::addOne (77 bytes)
113 6 java.math.BigInteger::squareToLen (172 bytes)
114 7 java.math.BigInteger::primitiveLeftShift (79 bytes)
116 1% java.math.BigInteger::multiplyToLen @ 138 (219 bytes)
121 8 java.math.BigInteger::montReduce (99 bytes)
126 9 sun.security.provider.SHA::implCompress (491 bytes)
138 10 java.lang.String::charAt (33 bytes)
139 11 java.util.ArrayList::ensureCapacity (58 bytes)
139 12 java.util.ArrayList::add (29 bytes)
139 2% phil.StrangeRaceConditionTest$Buffer::<init> @ 38 (62 bytes)
158 13 java.util.HashMap::indexFor (6 bytes)
159 14 java.util.HashMap::hash (23 bytes)
159 15 java.util.HashMap::get (79 bytes)
159 16 java.lang.Integer::valueOf (32 bytes)
168 17 s phil.StrangeRaceConditionTest::getBuffer (66 bytes)
168 18 s phil.StrangeRaceConditionTest::remove (10 bytes)
171 19 s phil.StrangeRaceConditionTest$Buffer::remove (34 bytes)
172 3% phil.StrangeRaceConditionTest::strangeRaceConditionTest @ 36 (76 bytes)
ERRORS //my little change
219 15 made not entrant java.util.HashMap::get (79 bytes)
有三个 OSR 替换(编译 ID 上带有 % 注释的替换)。我的猜测是第三个,即调用 remove() 的循环,是造成错误的原因。这可以通过位于工作目录中的具有以下内容的 .hotspot_compiler 文件从 JIT 中排除:
exclude phil/StrangeRaceConditionTest strangeRaceConditionTest
当你再次运行程序时,你会得到这个输出:
CompilerOracle: exclude phil/StrangeRaceConditionTest.strangeRaceConditionTest
73 1 java.lang.String::hashCode (64 bytes)
104 2 sun.nio.cs.UTF_8$Decoder::decodeArrayLoop (553 bytes)
110 3 java.math.BigInteger::mulAdd (81 bytes)
113 4 java.math.BigInteger::multiplyToLen (219 bytes)
118 5 java.math.BigInteger::addOne (77 bytes)
120 6 java.math.BigInteger::squareToLen (172 bytes)
121 7 java.math.BigInteger::primitiveLeftShift (79 bytes)
123 1% java.math.BigInteger::multiplyToLen @ 138 (219 bytes)
128 8 java.math.BigInteger::montReduce (99 bytes)
133 9 sun.security.provider.SHA::implCompress (491 bytes)
145 10 java.lang.String::charAt (33 bytes)
145 11 java.util.ArrayList::ensureCapacity (58 bytes)
146 12 java.util.ArrayList::add (29 bytes)
146 2% phil.StrangeRaceConditionTest$Buffer::<init> @ 38 (62 bytes)
165 13 java.util.HashMap::indexFor (6 bytes)
165 14 java.util.HashMap::hash (23 bytes)
165 15 java.util.HashMap::get (79 bytes)
166 16 java.lang.Integer::valueOf (32 bytes)
174 17 s phil.StrangeRaceConditionTest::getBuffer (66 bytes)
174 18 s phil.StrangeRaceConditionTest::remove (10 bytes)
### Excluding compile: phil.StrangeRaceConditionTest::strangeRaceConditionTest
177 19 s phil.StrangeRaceConditionTest$Buffer::remove (34 bytes)
324 15 made not entrant java.util.HashMap::get (79 bytes)
并且问题没有出现(至少在我所做的重复尝试中没有出现)。
此外,如果稍微更改 JVM 选项,可能会导致问题消失。使用以下任一方法都无法出现问题。
java -XX:+PrintCompilation -XX:CompileThreshold=100000 phil.StrangeRaceConditionTest
java -XX:+PrintCompilation -XX:FreqInlineSize=1 phil.StrangeRaceConditionTest
有趣的是,这两个的编译输出仍然显示了删除循环的 OSR。我的猜测(这是一个很大的猜测)是通过编译阈值延迟 JIT 或更改 FreqInlineSize 会在这些情况下导致 OSR 处理发生更改,从而绕过您可能遇到的错误。
有关 JVM 选项的信息,请参见此处。
有关-XX:+PrintCompilation 的输出以及如何弄乱 JIT 所做的事情的信息,请参见此处和此处。