字节数组转int出现不可预测值(Eclipse Temurin-17环境)
多线程下字节数组转int间歇性返回随机值问题
问题描述
运行多线程程序时出现异常:创建大量线程(10000个)处理同一字节数组,在高CPU占用或内存受限(如设置-Xms32m -Xmx32m)场景下,byteArrayToInt方法将字节数组特定段转为int时,间歇性返回随机值。预期liVersion值为1,但实际输出随机数;即使内存充足、线程数少(甚至单线程)仍会出现,只是频率降低。
环境信息
- 操作系统:Windows 10
- Java版本:Eclipse Temurin-17.0.10+11,该问题也在Eclipse Temurin-17.0.13+11、Amazon Corretto 17中复现,暂未在AdoptOpenJDK 11.0.11中发现。
代码说明
TestMain类中,compareInner方法复制字节数组的指定段([1,0]),调用byteArrayToInt转为int,预期结果为1,但间歇性输出错误值。调试时未发现问题,单线程单次调用compareInner不会触发问题。
复现条件
- 设置
-Xms32m -Xmx32m时可稳定复现; - 内存设为1024m时复现频率降低;
- 单线程场景下仍会偶现;
- 线程内多次调用
compareInner时触发。
代码片段
import java.util.Arrays; public class TestMain { private static int miParallelThreadCount = 10000; private static long mlExecutionIntervalInMillis=100; public static void main(String[] args) { System.out.println("Startin tornado at interval: " + mlExecutionIntervalInMillis + " with parallel threads: " + miParallelThreadCount); while(true) { //Runs the test code to reproduce the issue runTornado(); try { Thread.sleep(mlExecutionIntervalInMillis); } catch (Throwable lThrowable) { System.out.println("Task failed due to: " + lThrowable.getLocalizedMessage()); lThrowable.printStackTrace(); } } } private static void runTornado() { //Test byte array byte[] data = new byte[] { 65, 50, 1, 0, 1, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 16, 0, 53, -76, -66, 125, -115, 69, -111, 80, -81, -33, -62, 84, -59, -64, -114, 113, -92, -59, 37, 106, 85, -111, -100, -19, 114, 99, 18, -9, -62, -123, -75, -22, -40, 70, -70, -46, 119, 11, -17, -71, -93, -126, -28, 116, 104, -4, -45, -58, 121, 4, 50, 76, 46, 13, -91, -76, 37, -103, 101, 57, 54, 75, 83, -124 }; for(int i=0; i<miParallelThreadCount; ++i) { //This snippet executes the group of compareInner methods in new threads new Thread(() -> { try { compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); compareInner(data, 0, data.length); } catch (Throwable lThrowable) { System.out.println("Something went wrong: " + lThrowable.getLocalizedMessage()); lThrowable.printStackTrace(); } }).start(); } System.out.println("Ran tornado"); } private static byte[] compareInner(byte[] pTestData, int pStartIndex, int pLength) { //Copies the pTestData into new array as it is byte[] lbarrData = Arrays.copyOfRange(pTestData, 0, 0 + pTestData.length); //copies only 2nd and 3rd element from the array which is [1, 0] byte[] lbarrVer = Arrays.copyOfRange(lbarrData, 2, 4); //Converts the array [1, 0] into integer, which should always return 1 int liVersion = byteArrayToInt(lbarrVer); if(liVersion != 1) { System.out.println("Invalid Version : " + liVersion + " data array: " + Arrays.toString(lbarrData) + " header array: " + Arrays.toString(lbarrVer)); } return lbarrData; } public static final int byteArrayToInt(byte[] pSource) { int lValue = 0; for (int i = 0; i < pSource.length; i++) { lValue += (pSource[i] & 0xff) << (8 * i); } return lValue; } }
问题分析与解决
根本原因
这是JDK 17中C2编译器的优化bug,当JIT编译Arrays.copyOfRange相关代码时,出现内存访问错误,导致复制后的字节数组内容被意外篡改。内存受限或高并发场景下GC频繁触发,会更容易触发该优化bug。
解决方案
- 临时规避方案:
- 添加JVM参数禁用C2编译器:
-XX:-UseC2,仅保留C1编译器; - 替换
Arrays.copyOfRange为手动复制逻辑,避免触发有问题的JIT优化:private static byte[] manualCopyRange(byte[] src, int from, int to) { int length = to - from; byte[] dest = new byte[length]; System.arraycopy(src, from, dest, 0, length); return dest; }
- 添加JVM参数禁用C2编译器:
- 长期方案:升级至修复该bug的JDK版本,如Eclipse Temurin 17.0.14+或更高版本,官方已在后续维护版本中修复此问题。
相关讨论内容
该问题在Adoptium支持社区中被确认是JDK 17特有的C2编译器优化问题,JDK 11未受影响。问题根源是Arrays.copyOfRange的JIT编译代码出现内存引用错误,官方已发布修复补丁,升级JDK版本即可彻底解决;临时禁用C2可立即规避问题,但会损失部分性能。
内容的提问来源于stack exchange,提问作者Amey Sawant
相关产品推荐
相关产品推荐

