对于一个研究项目,我试图弄清楚单元测试(用 JUnit 编写)在不同上下文中执行时是否表现不同。为此,我做了两件事:首先,我使用自定义 JUnit 运行器运行程序的整个测试套件,然后使用相同的自定义 JUnit 运行器运行单个测试(为什么这有意义并不重要,只需接受它即可)现在)。当测试失败时,我会记录 JUnit 报告的整个异常堆栈跟踪,然后比较两次运行之间的堆栈跟踪。
这样做时,我偶然发现了一些我无法解释的奇怪现象。下面是以这种方式记录的两个堆栈跟踪的两个摘录。
运行整个测试套件时记录的堆栈跟踪:
org.fest.swing.edt.GuiActionRunner.resultOf(GuiActionRunner.java:126)
org.fest.swing.edt.GuiActionRunner.execute(GuiActionRunner.java:73)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.constructTestFrameInEDT(AbstractNumericDataTypeUITest.java:98)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.setUp(AbstractNumericDataTypeUITest.java:81)
sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) - sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:616)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
运行单个测试时记录的堆栈跟踪:
org.fest.swing.edt.GuiActionRunner.resultOf(GuiActionRunner.java:126)
org.fest.swing.edt.GuiActionRunner.execute(GuiActionRunner.java:73)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.constructTestFrameInEDT(AbstractNumericDataTypeUITest.java:98)
net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.AbstractNumericDataTypeUITest.setUp(AbstractNumericDataTypeUITest.java:81)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:616)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
两个堆栈跟踪都显示崩溃前的最后几个条目,从最后一个公共(public)语句开始。显然,在 JDK/VM 的反射实现中的某个地方,这两种情况之间的行为有所不同。
我的问题是为什么?
我推测这与虚拟机缓存早期反射调用的内容或其他内容有关,但我真的不知道。了解这一点很重要,因为我必须弄清楚这种情况是否会在每次运行单个测试时发生(因此我可以忽略它),或者这是否与我正在运行的具体测试。
我知道这相当模糊,但我们将不胜感激。
最佳答案
这是因为Sun/Oracle JVM(其他我不知道)优化了反射调用。从 1.4 开始,JVM 生成字节码来桥接对被调用方法的反射调用。因为并不是所有的事情在编译时都是已知的,所以它必须在运行时完成。
类(class)MethodAccessorGenerator生成 GenelatedMethodAccessor1
我从 NativeMethodAccessorImpl 的评论中发现的提示:
仅用于方法的前几次调用;之后,切换到基于字节码的实现
您是否在一个 Java VM 的生命周期中运行该套件和单个测试?
关于java - 当测试在完全相同的语句处崩溃时,为什么调用堆栈会不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12046114/