我正在使用JMockit来模拟System.currentMillis() 很少有调用返回模拟时间,但过了一段时间后,它开始返回原始时间 当我在禁用JIT后运行相同的操作时,它运行得很好。
答案 0 :(得分:3)
您显然对一个或多个组件内的当前时间有重要的依赖性。在这种情况下,您应该使用接口表达此依赖关系:
public interface TimeService {
long currentTimeMillis();
}
在您的真实代码中,您有一个使用System
方法的实现:
public final SystemTimeService implements TimeService {
@Override
public long currentTimeMillis() {
return System.currentTimeMillis();
}
}
注意,使用Java 8,您可以减少一些代码以更清晰地表达它(感谢@Holger):
public interface TimeService {
static final DEFAULT = System::currentTimeMillis;
long currentTimeMillis();
}
依赖于此时间服务的类应如下所示:
public final ClassThatDependsOnTimeService {
private final TimeService timeService;
public ClassThatDependsOnTimeService(TimeService timeService) {
this.timeService = timeService;
}
// other features omitted
}
现在他们可以用
喂养TimeService timeService = new SystemTimeService();
ClassThatDependsOnTimeService someObject = new ClassThatDependsOnTimeService(timeService);
或(Java 8):
ClassThatDependsOnTimeService someObject = new ClassThatDependsOnTimeService(TimeService.DEFAULT);
或任何依赖注入框架或其他。
在测试中,您不会模拟方法System.currentTimeMillis
,而是模拟接口TimeService
并将模拟注入到依赖类中。
答案 1 :(得分:1)
这是因为JVM中的JIT优化器不检查重新定义的方法(重新定义是通过JVM中的不同子系统完成的)。因此,最终JVM决定优化包含对System.currentTimeMillis()
的调用的代码,内联对native
Java方法的调用,以便它直接开始执行实际的本机方法。此时,优化器应检查当前是否重新定义currentTimeMillis()
,并在重新定义时放弃内联。但不幸的是,JDK工程师未能解释这种可能性。
如果你真的需要多次调用一个模拟的System.currentTimeMillis()
,那么唯一的解决方法就是使用-Xint
运行(这不是一个坏主意,因为它通常会减少总执行时间试运行)。