2015-05-10 2 views
3

Я использую JMockit для моделирования System.currentMillis().
Немногие призывы возвращают насмешливое время, но через какое-то время он начинает возвращаться к исходному времени.
Когда я запускаю то же самое после отключения JIT, он работает отлично.JMockit: Издевательство над apis возвращаются после somtime

ответ

1

Это происходит потому, что оптимизатор JIT в JVM не проверяет переопределенные методы (переопределение выполняется через другую подсистему в JVM). Таким образом, в конечном итоге JVM решает оптимизировать код, содержащий вызов, System.currentTimeMillis(), вставляя вызов метода Java native, чтобы он сразу начал выполнять фактический собственный метод. На этом этапе оптимизатор должен проверить, переопределено ли сейчас currentTimeMillis() или нет, и отказаться от вложения в случае его переопределения. Но, к сожалению, инженеры JDK не смогли объяснить эту возможность.

Если вам действительно нужно вызывать издеваемое System.currentTimeMillis() слишком много раз, единственное обходное решение действительно должно работать с -Xint (что не так уж плохо, поскольку обычно это сокращает общее время выполнения тестового прогона).

+0

Интересно. Является ли он специфичным для currentTimeMillis()? В противном случае весь Инструментарий пойдет на бросок, и это будет вероятностная функция. @Rogerio –

+1

Он влияет только на собственные методы, такие как 'currentTimeMillis()'. –

+0

Я не удивлюсь, если это применимо ко всем [* внутренним * методам] (https://wikis.oracle.com/display/HotSpotInternals/PerformanceTechniques#PerformanceTechniques-Intrinsics), независимо от того, есть ли у них «родной» или чистой реализации Java. Ожидается, что эти методы будут иметь четко определенную семантику. Например. 'Math.sqrt' не является« родным », но я думаю, что издевательство над ним не удастся, как только оптимизатор начнет работать ... – Holger

3

Очевидно, что у вас есть важная зависимость от текущего времени внутри одного или нескольких компонентов. В этом случае вы должны выразить эту зависимость с интерфейсом:

public interface TimeService { 
    long currentTimeMillis(); 
} 

В вашем реальном коде у вас есть реализация, которая использует System метод:

public final SystemTimeService implements TimeService { 
    @Override 
    public long currentTimeMillis() { 
     return System.currentTimeMillis(); 
    } 
} 

Note, с 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 и вводите макет в зависимости от классов.

+0

Это хороший совет. В Java 8 вы также можете добавить 'TimeService DEFAULT = System :: currentTimeMillis;' в 'interface', уменьшая код, необходимый для абстракции. – Holger

Смежные вопросы