Java 如何检查JVM执行堆栈以确定堆栈损坏的原因
我有一个JUnit测试,大致如下所示:Java 如何检查JVM执行堆栈以确定堆栈损坏的原因,java,windows,jvm,java-native-interface,Java,Windows,Jvm,Java Native Interface,我有一个JUnit测试,大致如下所示: double doubleValue = 100.0 + 1E-14; long longValue = nativeInvokeWrapper(doubleValue, ...); assertEquals(doubleValue, Double.longBitsToDouble(longValue)); 背景 函数nativeInvokeWrapper调用另一个Java函数,该函数反过来调用JNI函数,JNI函数最终调用\uu stdcall“C”函
double doubleValue = 100.0 + 1E-14;
long longValue = nativeInvokeWrapper(doubleValue, ...);
assertEquals(doubleValue, Double.longBitsToDouble(longValue));
背景
函数nativeInvokeWrapper
调用另一个Java函数,该函数反过来调用JNI函数,JNI函数最终调用\uu stdcall
“C”函数,并将doubleValue
作为参数传递。“C”函数返回一个64位的值,该值按位等于它接收到的双参数,并且该返回值将被传播出去,直到它被nativeInvokeWrapper
返回
关键是nativeInvokeWrapper
的预期返回值是一个long
,按位等于doubleValue
问题
- 当我单独运行JUnit测试时,它通过了
- 当我运行整个JUnit测试套件时,所有其他测试都通过了,但这个测试失败了
- 当我在上面的测试中设置了一个断点来运行整个JUnit测试套件并逐步完成它时,它工作得很好
longValue
的值两次,问题似乎就消失了。例如,如果我将测试代码更改为:
double doubleValue = 100.0 + 1E-14;
long longValue = nativeInvokeWrapper(doubleValue, ...);
long longValue2 = longValue;
double doubleValue2 = Double.longBitsToDouble(longValue2);
if (doubleValue != doubleValue2)
{
String msg = "d=" + doubleValue + ", d2=" + doubleValue2 + "\n" +
"l=" + longValue + ", l2=" + longValue2;
throw new RuntimeException(msg);
}
然后运行整个JUnit测试套件,它抛出:
java.lang.RuntimeException: d=100.00000000000001, d2=NaN
l=4636737291354636289, l2=
4636737291354636289
请注意,两个double
值不同,但long
值相同
当我在调试器中单步执行时,一切正常。这就好像通过在需要将longValue
传递给Double.longBitsToDouble()
方法之前强制“读取”它,状态损坏问题就消失了
救命啊!
我想要的是关于如何调试/确定根本原因的想法。如上所述,在常规Eclipse调试器中单步执行是没有用的,因为读取longValue
似乎可以解决问题
我还能看什么?我还可以使用哪些其他工具来查看Java线程执行堆栈的内部状态,以查看是否有内容已损坏
更新:如果我关闭JIT(使用
-Xint
运行JVM),一切都会正常工作。因此,这与JIT编译代码特定的一些假设或状态有关…那么基本上,assertEquals
会因为值不同而失败吗?如果你强制阅读,它会起作用吗?那么,当您打印两次值时会得到什么呢?如果你的假设是正确的,那么应该打印两个不同的值。遗憾的是,没有,我试过了。但是,如果我引入一个类型为double
的临时变量,您可以看到有两个不同的double值(我将更新上面的问题)。您是否可以共享nativeInvokeWrapper()
的代码和相应的JNI部分,以便我们可以尝试运行?您可能触发了一些JIT错误。您的JVM版本和操作系统/体系结构是什么?使用+PrintAssembly运行时,是否可以发布结果?您还可以尝试使用+PrintNMethods或+printsubcode来更好地了解JVM/本机领域。