JAVA代码在debug和run时运行结果不同的记录

		虽然在刚开始学习JAVA的时候我就遇到过一次这样的问题,不过当时没有注意把问题记录下来,和老师说了之后也只能不了了之。这次我又遇到了这样的问题,不得不说还是很吓人的,毕竟如果连debug工具都不能相信了那我们又该相信谁。
		JAVA版本是11,IDE是IDEA2019.3.1的社区版本,操作系统是win10专业版,程序要处理的问题是一个简单的算法课作业:
		两重循环,迭代变量i,j符合0<i<N以及0<j<N,循环内容任意,比如s=s+i-j;请记录下运行时间,输出当两重循环运行时间是0.1s, 1s, 10s时的N.
		思路是每次都记录下循环时间,用二分法让程序自己找出来这3个N。
		以下是源码:
		一个小工具类,用来输出结果到文本:
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class OutData
{
    public OutData(Integer n1,//最接近0.1s的N
                   Integer n2,//最接近1s的N
                   Integer n3,//最接近10s的N
                   StringBuffer useTime1,//n1的具体时间
                   StringBuffer useTime2,//n2的具体时间
                   StringBuffer useTime3//n3的具体时间
    ) throws IOException
    {
        File fileOfDataIn = new File("Result.txt");
        FileWriter fileWriter;
        if (!fileOfDataIn.exists())
        {
            fileOfDataIn.createNewFile();
            fileWriter = new FileWriter(fileOfDataIn.getName(), true);
        } else
        {
            fileWriter = new FileWriter(fileOfDataIn.getName(), false);
            fileWriter.write("");
        }
        fileWriter.write("N为:" + n1.toString() + "时,所用时间为:" + useTime1.toString() + "毫秒"
                +"\n"+"N为:" + n2.toString() + "时,所用时间为:" + useTime2.toString() + "毫秒"
                +"\n"+"N为:" + n3.toString() + "时,所用时间为:" + useTime3.toString() + "毫秒");
        fileWriter.close();
    }
}
	接下来是main类,为了节省运行时间,求1s和10s的两次先注释掉:
import java.io.IOException;
//后来我的解决办法是再加一个随机函数到循环体里面,这里先注释掉
//import java.util.Random;

public class DoubleCirculationMain
{
    public static void main(String[] args) throws IOException
    {
        int startN = 1000;
        StringBuffer useTime1 = new StringBuffer(),
                useTime2 = new StringBuffer(),
                useTime3 = new StringBuffer();
        //使用二分法自动找到用时分别接近0.1s、1s和10s的N值并输出
        int n1 = getClosestValue(100, startN, useTime1);
        int n2=0;//节省测试时间
        //int n2 = getClosestValue(1000, n1, useTime2);
        int n3=0;//节省测试时间
        //int n3 = getClosestValue(10000, n2, useTime3);
        new OutData(n1, n2, n3, useTime1, useTime2, useTime3);
    }

    private static int getClosestValue(int timeToApproach, int startN, StringBuffer useTimeBuffer)
    {
        int nLower = startN,
                increment = 10000,
                nHeigher = nLower + increment,
                nMedian = (nHeigher + nLower) / 2,
                forCaculate = 0;
        long startTime, endTime, costime = 0, costime2 = 0;
        //后来我的解决办法是再加一个随机函数到循环体里面,这里先注释掉
        //Random random = new Random();
        while (true)
        {//循环至目标处于上下限区间内
            nMedian = (nHeigher + nLower) / 2;//刷新中位数
            forCaculate = 0;
            startTime = System.currentTimeMillis();//开始计时
            for (int i1 = 0; i1 < nHeigher; i1++)
            {
                for (int i2 = 0; i2 < nHeigher; i2++)
                {
                    forCaculate = forCaculate + i1 - i2;
                    //后来我的解决办法是再加一个随机函数到循环体里面,这里先注释掉
                    //forCaculate = forCaculate + i1 - i2+random.nextInt(100);
                }
            }
            endTime = System.currentTimeMillis();//结束计时
            //一开始就防着JAVA虚拟机自动优化代码,所以故意调用次运行结果
            System.out.println(forCaculate);
            costime = endTime - startTime;
            if (costime < timeToApproach)
            {//如果上限小于目标,则下限取该值,然后上限增大。
                nHeigher += increment;
                nLower = nHeigher;
            } else
            {//如果上限大于目标,则退出循环,花费时间值是上限时间
                break;
            }
        }
        while (costime >= 1.01 * timeToApproach && nHeigher - nLower > 1)
        {//当花费时间超出1.01倍目标且上下限差距大于1时,继续逼近
            nMedian = (nHeigher + nLower) / 2;//刷新中位数。
            startTime = System.currentTimeMillis();//开始计时
            for (int i1 = 0; i1 < nMedian; i1++)
            {
                for (int i2 = 0; i2 < nMedian; i2++)
                {
                    forCaculate = forCaculate + i1 - i2;
                    //后来我的解决办法是再加一个随机函数到循环体里面,这里先注释掉
                    //forCaculate = forCaculate + i1 - i2+random.nextInt(100);
                }
            }
            endTime = System.currentTimeMillis();//结束计时
            //还是为了防着JAVA虚拟机自动优化代码,所以故意调用次运行结果
            System.out.println(forCaculate);
            costime2 = endTime - startTime;
            if (costime2 > timeToApproach)
            {//如果中位数也大于目标,则中位数变为新上限,上限花费时间更新
                nHeigher = nMedian;
                costime = costime2;
            } else
            {//如果中位数小于目标,则中位数变成新下限
                nLower = nMedian;
            }
        }
        useTimeBuffer.append(costime);//输出逼近处理后的上限时间
        return nHeigher;
    }
}

run输出的文本内容是:

JAVA代码在debug和run时运行结果不同的记录
在getClosestValue()方法中随意加个断点debug之后输出的文本内容是:

JAVA代码在debug和run时运行结果不同的记录

		首先,我已经反复确认几遍了,这个问题确实存在,rebuild也好,完全重启IDEA也好,重启windows也好,run和debug的结果就是不一样。
		其次,如果把断点加在main方法里,则debug的运行结果会和run是一样的,但是如果随意在二分法方法getClosestValue()中加断点就会出现debug的运行结果和run不同的问题,debug的结果是理论上应该得到的结果。
		我的直觉告诉我在循环体里加入一个随机过程,因为我怀疑在解释运行时,编译器判断出了两个重复的代码段,于是就直接调用了运行结果以优化程序的运行效率,或许这就是JAVA在执行重复调用的代码段时效率可以追上C++的秘诀吧。不过如果真的是这样的话,显然这忽略了我在程序运行时间上的验证性要求,最后导致了输出结果和理论不一样,所以我一往循环体里加入随机过程之后,这个问题就被解决了。
		另一点值得一提的奇怪现象是,在我每次测试完这段代码之后,如果此时关闭idea,则下次打开idea一定会在开始的时候卡住,但是重启windows之后idea又可以正常打开,所以这个问题可能出在idea有关?
		作为一个小白,目前不太想过于深究,因为问题可能出现的地方太多了,而我现在也整不明白,所以姑且先记在这里,如果有路过的朋友对这个问题有兴趣,可以在评论区说说自己的看法,或者可以在自己的电脑上重现一下,换个IDE再试之类的,也就两个类几十行代码。
JAVA代码在debug和run时运行结果不同的记录JAVA代码在debug和run时运行结果不同的记录 Eglusaxie 发布了2 篇原创文章 · 获赞 0 · 访问量 64 私信 关注
上一篇:Hash


下一篇:易语言高速去重复,精易微凉“吃尾”算法