Why does this code sometimes produce ArrayOutOfBoundsException? How is that even possible for 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)
This is a JIT compiler bug that has been introduced in JDK 8u20 as a side-effect of another fix:
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
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.
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.
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.
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With