如何在String.valueOf(int)中使用ArrayOutOfBoundsException?

为什么这段代码有时会产生ArrayOutOfBoundsException? String.valueOf(int)怎么可能呢?

 public static String ipToString(ByteString bs) { if (bs == null || bs.isEmpty()) { return null; } else { StringBuilder sb = new StringBuilder(); boolean started = false; for (Byte byt : bs) { if (started) { sb.append("."); } sb.append(String.valueOf(byt & 0xFF)); started = true; } return sb.toString(); } } java.lang.ArrayIndexOutOfBoundsException: -81914 at java.lang.Integer.getChars(Integer.java:458) at java.lang.Integer.toString(Integer.java:402) at java.lang.String.valueOf(String.java:3086) at com.mystuff.mypackage.ipToString(MyCode.java:1325) ... at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) 

更新

当发生这种情况时,我不知道字节的值,但似乎不应该有任何可能的字节值。

一旦它发生一次,每次调用然后错误输出相同的exception。

环境:

 java version "1.8.0_20" Java(TM) SE Runtime Environment (build 1.8.0_20-b26) Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode) 

这是JDK 8u20中引入的JIT编译器错误,作为另一个修复的副作用:
JDK-8042786

该问题与自动装箱消除优化有关。
解决方法是通过-XX:-EliminateAutoBox JVM标志关闭优化

看起来这个问题也存在于最新的JDK 9源代码库中。
我已经提交了错误报告: https : //bugs.openjdk.java.net/browse/JDK-8058847,其中包含100%可重现的最小测试用例。

我可以使用以下代码可靠地重现您的问题:

 public class Main { public static StringBuilder intToString(byte[] bs) { final StringBuilder sb = new StringBuilder(); boolean started = false; for (Byte byt : bs) { if (started) sb.append("."); sb.append(String.valueOf(byt & 0xFF)); started = true; } return sb; } public static void main(String[] args) { final byte[] bs = {-2, -1, 0, 1, 2}; while (true) intToString(bs); } } 

几乎可以肯定这个问题可以追溯到JIT编译器错误。 您的观察结果,一旦它第一次发生,它会在每次后续调用中可靠地发生,干净地指向JIT编译事件,该事件将错误代码引入代码路径。

如果您可以使用它,则可以激活诊断JVM选项,该选项将打印所有编译事件( -XX:PrintCompilation )。 然后,您可以将此类事件与exception开始出现的时刻相关联。

我在这里留下代码片段,因为它仍然应该比原始代码运行得更快 – 以内存为代价 – 但是请注意它实际上并没有解决问题。

 private static final String[] STRING_CACHE = new String[256]; static { for(int i = 0; i <= 255; i++) { STRING_CACHE[i] = String.valueOf(i); } } public static String ipToString(ByteString bs) { if (bs == null || bs.isEmpty()) { return null; } else { StringBuilder sb = new StringBuilder(); boolean started = false; for (Byte byt : bs) { if (started) { sb.append("."); } sb.append(STRING_CACHE[byt & 0xFF]); started = true; } return sb.toString(); } }