Java 8奇数计时/内存问题

Java 8奇数计时/内存问题,java,memory,jvm,java-8,timing,Java,Memory,Jvm,Java 8,Timing,我遇到了一个在运行Java8时可能产生的相当奇怪的问题。问题的出现就好像JVM本身发生了某种定时错误。它本质上是间歇性的,但很容易复制(至少在我的测试环境中)。问题是,在某些情况下,显式设置的数组值被销毁并替换为0.0。具体地说,在下面的代码中,array[0]在newdouble(r.nextDouble())行之后计算为0.0。然后,如果您立即再次查看数组[0]的内容,它现在将显示正确的值1.0。运行此测试用例的示例输出为: claims array[0] != 1.0....array[0

我遇到了一个在运行Java8时可能产生的相当奇怪的问题。问题的出现就好像JVM本身发生了某种定时错误。它本质上是间歇性的,但很容易复制(至少在我的测试环境中)。问题是,在某些情况下,显式设置的数组值被销毁并替换为0.0。具体地说,在下面的代码中,
array[0]
newdouble(r.nextDouble())行之后计算为0.0。然后,如果您立即再次查看
数组[0]
的内容,它现在将显示正确的值1.0。运行此测试用例的示例输出为:

claims array[0] != 1.0....array[0] = 1.0
claims array[0] now == 1.0...array[0] = 1.0`
我运行的是64位Windows 7,并且能够在Eclipse中以及使用JDK 1.8_45、1.8_51和1.8_60从命令行编译时重现此问题。我无法生成运行1.7_51的问题。同样的结果已经在另一个64位Windows7机器上演示过

这个问题出现在一个大型的、不平凡的软件中,但我已经设法将它浓缩为几行代码。下面是一个演示该问题的小测试用例。这是一个看起来很奇怪的测试用例,但似乎都是导致错误的必要条件。不需要使用
Random
——我可以用任何双精度值替换所有
r.nextDouble()
,并演示问题。有趣的是,如果
someArray[0]=.45
替换为
someArray[0]=r.nextDouble(),我无法复制该问题(尽管
.45
没有什么特别之处)。Eclipse调试也无济于事——它改变了足够多的时间,使其不再发生。即使是位置正确的
System.err.println()
语句也会导致问题不再出现

同样,问题是间歇性的,因此要重现问题,可能必须多次运行此测试用例。我认为在获得上面所示的输出之前,我最多需要运行10次。在Eclipse中,我在运行后给它一到两秒钟的时间,如果没有发生,就杀死它。从相同的命令行运行它,如果没有退出并重试。看来,如果它要发生,它会很快发生

我以前也遇到过类似的问题,但它们都是线程问题。我不知道这里发生了什么-我甚至看过字节码(顺便说一下,字节码在1.7_51和1.8_45之间是相同的)

你知道这里发生了什么吗

import java.util.Random;

public class Test { 
    Test(){
        double array[] = new double[1];     
        Random r = new Random();

        while(true){
            double someArray[] = new double[1];         
            double someArray2 [] = new double [2];

            for(int i = 0; i < someArray2.length; i++) {
                someArray2[i] = r.nextDouble();
            }

            // for whatever reason, using r.nextDouble() here doesn't seem
            // to show the problem, but the # you use doesn't seem to matter either...

            someArray[0] = .45;

            array[0] = 1.0;

            // commented out lines also demonstrate problem
            new Double(r.nextDouble());
            // new Float(r.nextDouble();
            // double d = new Double(.1) * new Double(.3);
            // double d = new Double(.1) / new Double(.3);
            // double d = new Double(.1) + new Double(.3);
            // double d = new Double(.1) - new Double(.3);

            if(array[0] != 1.0){
                System.err.println("claims array[0] != 1.0....array[0] = " + array[0]);

                if(array[0] != 1.0){
                    System.err.println("claims array[0] still != 1.0...array[0] = " + array[0]);
                }else {
                    System.err.println("claims array[0] now == 1.0...array[0] = " + array[0]);
                }

                System.exit(0);
            }else if(r.nextBoolean()){
                array = new double[1];
            }
        }
    }

    public static void main(String[] args) {
        new Test();
    }
}
import java.util.Random;
公共类测试{
测试(){
双数组[]=新的双数组[1];
随机r=新随机();
while(true){
double someArray[]=新的double[1];
double Somerray2[]=新的double[2];
for(int i=0;i
更新:似乎我的原始答案不正确,OnStackReplacement在这个特定的案例中揭示了问题,但原始错误在转义分析代码中。转义分析是一个编译器子系统,它确定对象是否从给定的方法转义。非转义对象可以是scala在我们的测试中,转义分析确实很重要,因为几个创建的对象肯定不会转义该方法

我下载并安装了这个bug,注意到它消失了。但是在JDK 9 early access build 82中它仍然存在。b82和b83之间只显示了一个相关的bug修复(如果我错了请纠正我):“使用内联和转义分析对复杂代码进行不正确的JIT编译”。提交的有点类似:大循环、多个框(类似于我们测试中的一个元素数组),这会导致框内值的突然变化,因此结果会变得不正确(没有崩溃,没有异常,只是不正确的值).在我们的案例中,据报道,问题在8u40之前没有出现。问题非常简单:逸出分析源中只有一行变化

根据OpenJDK bug tracker,修复程序已经发布到JDK 8u72分支,该分支将于2016年1月发布。现在将此修复程序向后移植到即将发布的版本似乎为时已晚

建议的解决方法是禁用转义分析(-XX:-DoEscapeAnalysis)或禁用消除分配优化(-XX:-EliminateAllocations)。因此@apangin比我更接近答案

以下是原始答案


首先,我无法重现JDK 8u25的问题,但可以在JDK 8u40和8u60上重现:有时它会运行
...
     91   37 %     3       Test::<init> @ 28 (194 bytes)
Installing osr method (3) Test.<init>()V @ 28
     93   38       3       Test::<init> (194 bytes)
Installing method (3) Test.<init>()V 
     94   39 %     4       Test::<init> @ 16 (194 bytes)
Installing osr method (4) Test.<init>()V @ 16
    102   40 %     4       Test::<init> @ 28 (194 bytes)
    103   39 %     4       Test::<init> @ -2 (194 bytes)   made not entrant
...
Installing osr method (4) Test.<init>()V @ 28
    113   37 %     3       Test::<init> @ -2 (194 bytes)   made not entrant
claims array[0] != 1.0....array[0] = 1.0
claims array[0] now == 1.0...array[0] = 1.0
...
Installing method (3) Test.<init>()V 
     88   43 %     4       Test::<init> @ 28 (194 bytes)
Installing osr method (4) Test.<init>()V @ 28
    100   40 %     3       Test::<init> @ -2 (194 bytes)   made not entrant
   4592   44       3       java.lang.StringBuilder::append (8 bytes)
...
import java.util.Random;

public class Test2 {
    private static void doTest(double[] array, Random r) {
        double someArray[] = new double[1];
        double someArray2[] = new double[2];

        for (int i = 0; i < someArray2.length; i++) {
            someArray2[i] = r.nextDouble();
        }

        ... // rest of your code
    }

    Test2() {
        double array[] = new double[1];
        Random r = new Random();

        while (true) {
            doTest(array, r);
        }
    }

    public static void main(String[] args) {
        new Test2();
    }
}
int i=0;
someArray2[i++] = r.nextDouble();
someArray2[i++] = r.nextDouble();