Java JRE 致命错误:乘法过多

19

我试图测试Math.pow()的速度,与手动相乘,但遇到了以下错误:

Java运行时环境检测到致命错误:

EXCEPTION_ACCESS_VIOLATION(0xc0000005),pc=0x000000005ac46888,pid=1508,tid=6016

JRE版本:Java(TM) SE Runtime Environment(8.0_25-b18)(构建1.8.0_25-b18)
Java VM:Java HotSpot(TM) 64位Server VM(25.25-b02 mixed mode windows-amd64 compressed oops)
问题所在帧:
V [jvm.dll+0x496888]

未能写入核心转储。 在Windows客户端版本中,默认情况下未启用Minidumps。

生成该错误的代码:

long t = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
    double val = i*i*i*i*i /* 256 times *i */ *i*i*i;
    sum ^= Double.doubleToLongBits(val);
}
System.out.println((System.nanoTime() - t) / 10000000);

我知道这是一个极端的情况,但是这确实是有效的代码,最糟糕的情况应该是值中有无穷大,而不是JRE崩溃。这是否真的是Oracle描述的标准行为,还是只是一个没有人想修复的错误,因为如果你看到了它,你真的是个坏人。

记录一下,使用NetBeans 8.0.2运行。

更新1

看起来问题在于乘数的数量级。

long t = System.nanoTime();
for(int j = 0; j < 10000000; j++) {
    int i = j % 50;
    double val = i*i*i*i*i /* 256 times *i */ *i*i*i;
    sum ^= Double.doubleToLongBits(val);
}
System.out.println((System.nanoTime() - t) / 10000000);

希望一切顺利。

更新2

尝试从控制台运行:

java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

并且已经通过了,所以我认为这可能是特定JRE或NetBeans的问题。


1
如果你使用普通的javac编译代码并从命令行运行它呢? - biziclop
2
Netbeans与该问题无关。Netbeans只是让您轻松地在要求使用的JVM中运行程序。 - BillRobertson42
1
似乎是JRE 8.0_25中的一个错误,已经在8.0_45版本中得到修复。 - Vojtěch Kaiser
2
您永远不会在值中得到Inf。这些是整数乘法。您只会得到整数溢出和乘法的整数结果,然后转换为double。这段代码与Math.pow()无法比较。它不应该失败,但它并不能做到您想要的。我也不理解sum ^=部分。 - user207421
@EJP,我听说编译器喜欢丢弃无用的代码,并且使用XOR和print是确保这种情况不会发生的一种非常便宜的方法。当然,我不能百分之百确定在任何编译器中都可能发生这种情况,但是在我的其他代码中已经发生过了。如果编译器看到您的变量内容没有离开作用域或以某种方式传递到其外部,则可能开始删除对其执行的操作。至于pow比较,后来意识到double和int乘法并不完全可比。我试图测试的是Math.pow(int,+int)的低效性。 - Vojtěch Kaiser
显示剩余6条评论
3个回答

1
这绝对看起来像是JVM的一个bug。这更适合作为一个错误报告而不是在SO上提问。请参见http://bugreport.java.com/

0

尝试使用BigInteger。

import java.math.BigInteger;

public class hackerrank {
    public static void main (String args[]){
        BigInteger x = new BigInteger("10000000");
        BigInteger sum = BigInteger.ZERO;
        long t = System.nanoTime();
        for (BigInteger i = BigInteger.ONE; i.compareTo(x) == -1; i = i.add(BigInteger.ONE)) {
            BigInteger j = i.remainder(new BigInteger("50"));
            BigInteger val = j.pow(256);
            sum = sum.xor(val);
        }
        System.out.println((System.nanoTime() - t) / 10000000);
    }
}

输出:4083


你完全错过了这个问题的重点。这不是关于得到原始任务的答案,而是找出为什么完全有效的代码会导致JVM崩溃。此外,使用BigInteger进行测量并不符合我正在检查的内容,即从哪个点开始通过扩展乘法避免“Math.pow”不再有益。我想BigInteger并不是你每天都会用到的数字类型。 - Vojtěch Kaiser

0

这对我来说很好用。 你在哪里遇到了问题?通过减少乘法或循环次数来分离问题。50个乘法可以工作吗?

long sum=0;
long t = System.nanoTime();
for (int i = 1; i < 10000000; i++) {
    double val = i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i
                    *i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i
                    *i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i*i; 
            // set of 10 *i*i*i*i*i*i*i*i*i*i
    sum ^= Double.doubleToLongBits(val);
}
System.out.println(sum);
System.out.println((System.nanoTime() - t) / 10000000);

jre1.8.0_51

输出:

    -32067153335156736
    614

即使将迭代次数增加10倍,但速度仍然非常慢。时间为6020。


在问题下的评论中,我们得出结论它发生在JRE 8.0_25上,并且乘法的数量是不同的。 - Vojtěch Kaiser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接