ArrayOutOfBoundsException 在 String.valueOf(int) 中是如何可能的?

2022-09-01 14:33:27

为什么此代码有时会生成 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)

更新

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

一旦它发生一次,每次调用都会出错,并出现相同的异常。

环境:

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)

答案 1

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

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

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


答案 2

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

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


推荐