We have a Java application that needs to run, among other environments, on a Virtual (Hyper-V) Windows 2012 R2 Server. When executed on this virtual Windows server, it seems to experience weird timing issues. We've traced the issue to erratic scheduling in a Java scheduled executor:
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);
}
This code, which should run the TimeRunnable every 10ms, produces results such as these on the server:
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
While on other machines, including heavily loaded virtual Linux boxes, as well as some windows desktops, a typical run looks like this:
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
We don't have a lot of experience with Windows Server and Hyper-V, so can anyone offer an explanation for this phenomena? It it a Windows Server issue? Hyper-V? A Java bug on these platforms? Is there a solution?
EDIT: A colleague has written a C# version of the same program:
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();
}
Here's an updated (partial) screenshot of both applications working side by side on the virtual windows server:
EDIT: A few other variants of the Java program all produce (pretty much) the same output:
- A variant in which
System.nanoTime()
was replaced withSystem.currentTimeMillis()
- A variant in which
System.out.println()
was replaced with a periodically printed StringBuilder - A variant in which the scheduling mechanism was replaced with a single thread that times itself through
Thread.sleep()
- A variant in which the
lastRunAt
is volatile