java - 为什么 System.nanoTime() 和 System.currentTimeMillis() 如此迅速地分开?

标签 java windows datetime time

出于诊断目的,我希望能够在长时间运行的服务器应用程序中检测系统时钟的变化。由于 System.currentTimeMillis() 基于挂钟时间,而 System.nanoTime() 基于独立于 (*) 挂钟时间的系统定时器,我以为我可以使用这些值之间差异的变化来检测系统时间变化。

我编写了一个快速测试应用程序,以查看这些值之间的差异有多稳定,令我惊讶的是,这些值立即以每秒几毫秒的水平出现差异。有几次我看到了更快的分歧。这是在带有 Java 6 的 Win7 64 位桌面上。我还没有在 Linux(或 Solaris 或 MacOS)下尝试过这个测试程序来查看它的性能。对于此应用程序的某些运行,差异是正的,对于某些运行,它是负的。这似乎取决于桌面在做什么,但很难说。

public class TimeTest {
  private static final int ONE_MILLION  = 1000000;
  private static final int HALF_MILLION =  499999;

  public static void main(String[] args) {
    long start = System.nanoTime();
    long base = System.currentTimeMillis() - (start / ONE_MILLION);

    while (true) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // Don't care if we're interrupted
      }
      long now = System.nanoTime();
      long drift = System.currentTimeMillis() - (now / ONE_MILLION) - base;
      long interval = (now - start + HALF_MILLION) / ONE_MILLION;
      System.out.println("Clock drift " + drift + " ms after " + interval
                         + " ms = " + (drift * 1000 / interval) + " ms/s");
    }
  }
}

Thread.sleep() 时间的不准确以及中断应该与计时器漂移完全无关。

这两个 Java“系统”调用都旨在用作测量——一个用于测量挂钟时间的差异,另一个用于测量绝对时间间隔,因此当实时时钟未更改时,这些值应该以非常接近相同的速度变化,对吧?这是 Java 中的错误、弱点还是失败?操作系统或硬件中是否存在阻止 Java 更准确的东西?

我完全预计这些独立测量之间会有一些漂移和抖动 (**),但我预计每天的漂移不到一分钟。每秒 1 毫秒的漂移,如果单调,几乎是 90 秒!我观察到的最坏情况漂移可能是那个的十倍。每次我运行这个程序时,我都会在第一次测量时看到漂移。到目前为止,我运行程序的时间没有超过 30 分钟。

由于抖动,我希望在打印的值中看到一些小的随机性,但在程序的几乎所有运行中,我看到差异稳步增加,通常每秒增加 3 毫秒和几倍不止于此。

是否有任何版本的 Windows 具有类似于 Linux 的机制,可以调整系统时钟速度以缓慢地将时间时钟与外部时钟源同步?这样的事情会影响两个计时器,还是只影响挂钟计时器?

(*) 我知道在某些架构上,System.nanoTime() 必须使用与 System.currentTimeMillis() 相同的机制。我还认为可以公平地假设任何现代 Windows 服务器都不是这样的硬件架构。这是一个糟糕的假设吗?

(**) 当然,System.currentTimeMillis() 通常会有比 System.nanoTime() 大得多的抖动,因为它的粒度不是 1 毫秒大多数系统。

最佳答案

您可能会发现 this Sun/Oracle blog post about JVM timers感兴趣。

以下是那篇文章中关于 Windows 下的 JVM 计时器的几段:

System.currentTimeMillis() is implemented using the GetSystemTimeAsFileTime method, which essentially just reads the low resolution time-of-day value that Windows maintains. Reading this global variable is naturally very quick - around 6 cycles according to reported information. This time-of-day value is updated at a constant rate regardless of how the timer interrupt has been programmed - depending on the platform this will either be 10ms or 15ms (this value seems tied to the default interrupt period).

System.nanoTime() is implemented using the QueryPerformanceCounter / QueryPerformanceFrequency API (if available, else it returns currentTimeMillis*10^6). QueryPerformanceCounter(QPC) is implemented in different ways depending on the hardware it's running on. Typically it will use either the programmable-interval-timer (PIT), or the ACPI power management timer (PMT), or the CPU-level timestamp-counter (TSC). Accessing the PIT/PMT requires execution of slow I/O port instructions and as a result the execution time for QPC is in the order of microseconds. In contrast reading the TSC is on the order of 100 clock cycles (to read the TSC from the chip and convert it to a time value based on the operating frequency). You can tell if your system uses the ACPI PMT by checking if QueryPerformanceFrequency returns the signature value of 3,579,545 (ie 3.57MHz). If you see a value around 1.19Mhz then your system is using the old 8245 PIT chip. Otherwise you should see a value approximately that of your CPU frequency (modulo any speed throttling or power-management that might be in effect.)

关于java - 为什么 System.nanoTime() 和 System.currentTimeMillis() 如此迅速地分开?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5839152/

相关文章:

java - 当前一个语句失败时确保语句执行的干净方法

html - 在组织模式 html 导出中修改日期

python - 如何在 Python/Pandas 中将变量设置为 "Today' s"date

java - 下划线转为驼峰式大小写,某些前缀除外

java - 如何强制 Hibernate 将日期返回为 java.util.Date 而不是 Timestamp?

java - 为什么包含 JAVA_OPTS Xmx 时 Tomcat 不启动?

C# DriveInfo 类导致窗口弹出错误

windows - Dev-C++为错误版本的Windows创建EXE?

python - 如何解析 ISO 8601 格式的日期?

java - Java中可变大小的数据类型?