将字节数组转换为 int 时出现不可预测的值 |采用OpenJDK 17.0.10

问题描述 投票:0回答:1

问题陈述:

我在运行以下多线程程序时遇到问题。该程序生成大量线程(10,000 个)来处理相同的字节数组值。在 CPU 使用率较高和应用内存限制时(例如,设置 -Xms32m -Xmx32m),此问题会变得更加频繁。

方法 byteArrayToInt 应该将字节数组的特定段转换为整数。然而,它会间歇性地产生不正确的值。 liVersion 的预期值为 1,但改为观察随机值。即使程序以足够的内存和较少的线程数(甚至单线程)执行,也会出现此问题,但频率明显降低。

pTestData 的第 2 个到第 4 个值([1, 0])的整数转换应该为 1,但它在输出中间歇性地给出随机值。

环境:

操作系统:Windows 10

Java版本:采用OpenJDK 17.0.10

代码片段:

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 2ndand 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;
    }
}
  • 我尝试使用 32m xms 和 xmx 运行此代码,问题是可重现的。
  • 即使 xms 和 xmx 为 1024,问题也可以重现,但频率较低。
  • 问题出现在单线程 (miParallelThreadCount = 1) 中,但频率较低。
  • 对代码进行调试时尚未发现问题。
  • “compareInner(enc, 0, enc.length);”时未观察到问题每个线程执行仅调用一次方法。
  • AdoptOpenJDK 11.0.11 上尚未观察到问题。
java arrays memory-management bit-manipulation byte
1个回答
0
投票

你只需要更好地调试它。

开始运行这个单线程。然后添加一个计数器

static int x = -1; // add counter

    public static final int byteArrayToInt(byte[] pSource)
    {
        x++ // increment counter
        int lValue = 0;
        for (int i = 0; i < pSource.length; i++)
        {
            lValue += (pSource[i] & 0xff) << (8 * i);
        }
        // add a print out
        System.err.println("lValue: " + lValue + " counter: " + x);
        return lValue;
    }

一旦找到问题发生的位置(对于单线程,每次运行都应该相同),在计数器值上设置断点,例如

    public static final int byteArrayToInt(byte[] pSource)
    {
        if (x == 5) { // replace 5
          System.out.println("debug"); //add a breakpoint right here
        }
        int lValue = 0;
        for (int i = 0; i < pSource.length; i++)
        {
            lValue += (pSource[i] & 0xff) << (8 * i);
        }
        return lValue;
    }
}

这应该可以帮助您隔离单线程问题

© www.soinside.com 2019 - 2024. All rights reserved.