У нас есть приложение Java, которое должно запускаться среди других сред на виртуальном (Hyper-V) Windows 2012 R2 Server. При выполнении на этом виртуальном сервере Windows, похоже, возникают странные проблемы времени. Мы проследили эту проблему в неустойчивом планировании в Java-планировщике:
public static class TimeRunnable implements Runnable {
private long lastRunAt;
@Override
public void run() {
long now = System.nanoTime();
System.out.println(TimeUnit.NANOSECONDS.toMillis(now - lastRunAt));
lastRunAt = now;
}
}
public static void main(String[] args) {
ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
exec.scheduleAtFixedRate(new TimeRunnable(), 0, 10, TimeUnit.MILLISECONDS);
}
Этот код, который должен запускать TimeRunnable каждые 10 мс, дает такие результаты на сервере:
12
15
2
12
15
0
14
16
2
12
140
0
0
0
0
0
0
0
0
0
0
0
0
1
0
7
15
0
14
16
2
12
15
2
12
1
123
0
0
0
В то время как на других машинах, в том числе сильно загруженных виртуальных ящиках Linux, а также на некоторых рабочих столах Windows, типичный запуск выглядит следующим образом:
9
9
10
9
10
9
10
10
9
10
9
9
10
10
9
9
9
9
10
10
9
9
10
10
9
9
10
9
10
10
10
11
8
9
10
9
10
9
10
10
9
9
9
10
9
9
10
10
10
9
10
У нас нет большого опыта работы с Windows Server и Hyper-V, так может ли кто-нибудь объяснить это явление? Это проблема Windows Server? Hyper-V? Явная ошибка на этих платформах? Есть ли решение?
EDIT: коллега написал версию С# той же программы:
private static Stopwatch stopwatch = new Stopwatch();
public static void Main()
{
stopwatch.Start();
Timer timer = new Timer(callback, null, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(10));
}
private static void callback(object state)
{
stopwatch.Stop();
TimeSpan span = stopwatch.Elapsed;
Console.WriteLine((int)span.TotalMilliseconds);
stopwatch.Restart();
}
Здесь обновленный (частичный) снимок экрана обоих приложений, работающих бок о бок на виртуальном сервере Windows:
EDIT: Несколько других вариантов программы Java производят (в значительной степени) один и тот же вывод:
- Вариант, в котором
System.nanoTime()
был заменен наSystem.currentTimeMillis()
- Вариант, в котором
System.out.println()
был заменен периодически напечатанным StringBuilder - Вариант, в котором механизм планирования был заменен одним потоком, который сам по себе проходит через
Thread.sleep()
- Вариант, в котором
lastRunAt
является изменчивым