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

时间:2023-01-23 17:20:10

Why does this code sometimes produce ArrayOutOfBoundsException? How is that even possible for String.valueOf(int)?

为什么这段代码有时会产生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)

Updates

I don't know the value of the byte when this occurs, but it doesn't seem like it should be possible for any possible value of byte.

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

Once it happens once, every invocation then errors out with the same exception.

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

Environment:

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)

3 个解决方案

#1


18  

This is a JIT compiler bug that has been introduced in JDK 8u20 as a side-effect of another fix:
JDK-8042786

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

The problem is related to auto-boxing elimination optimization.
The work-around is to switch the optimization off by -XX:-EliminateAutoBox JVM flag

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

Looks like the problem also exists in the most recent JDK 9 source base.
I've submitted the bug report: https://bugs.openjdk.java.net/browse/JDK-8058847 with 100% reproducible minimal test case included.

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

#2


7  

I can reliably reproduce your issue with this code:

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

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);
  }
}

The issue will almost certainly be traced to a JIT compiler bug. Your observation that, once it happens the first time, it happens reliably on every subsequent call, points cleanly to a JIT compilation event which introduces the buggy code into the codepath.

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

If that's available to you, you could activate diagnostic JVM options which will print all compilation events (-XX:PrintCompilation). Then you may be able to correlate such an event with the moment when the exception starts appearing.

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

#3


7  

I am leaving the code snippet here, as it still ought to be run faster than the original code - at a cost of memory - but be advised it doesn't actually fix the problem.

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

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();
  }
}

#1


18  

This is a JIT compiler bug that has been introduced in JDK 8u20 as a side-effect of another fix:
JDK-8042786

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

The problem is related to auto-boxing elimination optimization.
The work-around is to switch the optimization off by -XX:-EliminateAutoBox JVM flag

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

Looks like the problem also exists in the most recent JDK 9 source base.
I've submitted the bug report: https://bugs.openjdk.java.net/browse/JDK-8058847 with 100% reproducible minimal test case included.

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

#2


7  

I can reliably reproduce your issue with this code:

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

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);
  }
}

The issue will almost certainly be traced to a JIT compiler bug. Your observation that, once it happens the first time, it happens reliably on every subsequent call, points cleanly to a JIT compilation event which introduces the buggy code into the codepath.

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

If that's available to you, you could activate diagnostic JVM options which will print all compilation events (-XX:PrintCompilation). Then you may be able to correlate such an event with the moment when the exception starts appearing.

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

#3


7  

I am leaving the code snippet here, as it still ought to be run faster than the original code - at a cost of memory - but be advised it doesn't actually fix the problem.

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

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();
  }
}