出于诊断目的,我希望能够在长时间运行的服务器应用程序中检测系统时钟的变化。由于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毫秒。
答案 0 :(得分:11)
您可能会感兴趣this Sun/Oracle blog post about JVM timers。
以下是该文章中有关Windows下JVM计时器的几个段落:
System.currentTimeMillis()
是使用GetSystemTimeAsFileTime
方法实现的,该方法基本上只读取Windows维护的低分辨率时间值。根据报告的信息,读取这个全局变量自然非常快 - 大约6个周期。无论定时器中断如何编程,这个时间值都以恒定速率更新 - 取决于平台,这将是10ms或15ms(此值似乎与默认中断周期相关)。
System.nanoTime()
是使用QueryPerformanceCounter
/QueryPerformanceFrequency
API实现的(如果可用,否则返回currentTimeMillis*10^6
)。QueryPerformanceCounter
(QPC)以不同的方式实现,具体取决于它运行的硬件。通常,它将使用可编程间隔定时器(PIT)或ACPI电源管理定时器(PMT)或CPU级时间戳计数器(TSC)。访问PIT / PMT需要执行慢速I / O端口指令,因此QPC的执行时间大约为微秒。相反,读取TSC大约为100个时钟周期(从芯片读取TSC并将其转换为基于工作频率的时间值)。您可以通过检查QueryPerformanceFrequency是否返回3,579,545(即3.57MHz)的签名值来判断您的系统是否使用ACPI PMT。如果您看到大约1.19Mhz的值,那么您的系统正在使用旧的8245 PIT芯片。否则,您应该看到一个大约相当于CPU频率的值(模数可能有效的任何速度限制或电源管理。)
答案 1 :(得分:7)
我不确定这实际上有多大帮助。但这是Windows / Intel / AMD / Java世界中一个积极变化的领域。在几个(至少10年)内,对准确和精确的时间测量的需求已经很明显。英特尔和AMD都通过改变TSC的工作方式做出了回应。两家公司现在都有一种名为 Invariant-TSC 和/或 Constant-TSC 的东西。
结帐rdtsc accuracy across CPU cores。引自osgx(指英特尔手册)。
“16.11.1不变的TSC
较新处理器中的时间戳计数器可能支持增强,称为不变TSC。处理器对不变TSC的支持由PUID.80000007H:EDX [8]表示。
不变的TSC将在所有ACPI P-,C-中以恒定速率运行。和T状态。这是向前发展的建筑行为。在具有不变TSC支持的处理器上,OS可以将TSC用于挂钟计时器服务(而不是ACPI或HPET计时器)。 TSC读取效率更高,不会产生与环转换或访问平台资源相关的开销。“
另见http://www.citihub.com/requesting-timestamp-in-applications/。引自作者
如果CPUID 8000_0007.edx [8] = 1,则确保TSC速率在所有P状态,C状态和停止授权转换(例如STPCLK限制)之间保持不变;因此,TSC适合用作时间源。
处理器对不变TSC的支持由CPUID.80000007H:EDX [8]指示。不变的TSC将在所有ACPI P-,C-中以恒定速率运行。和T状态。这是向前发展的建筑行为。在具有不变TSC支持的处理器上,OS可以将TSC用于挂钟计时器服务(而不是ACPI或HPET计时器)。 TSC读取效率更高,不会产生与环转换或访问平台资源相关的开销。“
现在非常重要的一点是,最新的JVM似乎正在利用新的可靠TSC机制。没有多少在线显示这一点。但是,请查看http://code.google.com/p/disruptor/wiki/PerformanceResults。
“为了测量延迟,我们采用三级流水线并生成低于饱和度的事件。这是通过在注入事件之前等待1微秒然后注入下一个并重复5000万次来实现的。要达到这个精度水平的时间需要使用来自CPU的时间戳计数器。我们选择具有不变TSC的CPU,因为较旧的处理器由于节能和睡眠状态而受到频率变化的影响.Intel Nehalem和后来的处理器使用可由最新Oracle访问的不变TSC在Ubuntu 11.04上运行的JVM。此测试没有使用CPU绑定“
请注意,“Disruptor”的作者与处理Azul和其他JVM的人有密切联系。
另请参阅“幕后的Java飞行记录”。本演示文稿提到了新的不变TSC指令。
答案 2 :(得分:2)
“返回最精确的可用系统计时器的当前值,以纳秒为单位。
“此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。返回的值表示纳秒,因为某些固定但是任意时间(可能在未来,所以值可能这种方法提供纳秒精度,但不一定是纳秒精度。不能保证值的变化频率。连续调用的差异超过大约292年(2 ** 63纳秒)将无法准确计算经过的时间由于数字溢出。“
请注意,它表示“精确”,而不是“准确”。
这不是“Java中的错误”或任何“bug”。这是一个定义。 JVM开发人员四处寻找系统中最快的时钟/定时器并使用它。如果那与系统时钟锁定步骤那么好,但如果不是,那就是cookie崩溃的方式。比如说,计算机系统将具有准确的系统时钟,但内部具有更高速率的定时器,这与CPU时钟速率或某些此类速率相关,这完全合情合理。由于时钟频率通常会发生变化以最大限度地降低功耗,因此内部定时器的增量速率会发生变化。
答案 3 :(得分:2)
System.currentTimeMillis()
和System.nanoTime()
不一定由提供
相同的硬件。 System.currentTimeMillis()
支持GetSystemTimeAsFileTime()
拥有100ns的分辨率元素。它的来源是系统计时器。 System.nanoTime()
由系统的高性能计数器支持。有各种各样的硬件
提供这个柜台。因此,其分辨率因底层硬件而异。
在任何情况下都不能假设这两个来源同相。测量这两个值
相互对抗将披露不同的运行速度。如果System.currentTimeMillis()
的更新被视为真正的时间进度,System.nanoTime()
的输出有时可能会更慢,有时更快,也会变化。
必须进行仔细校准才能锁定这两个时间源。
可以找到这两个时间源之间关系的更详细描述 在Windows Timestamp Project。
答案 4 :(得分:2)
任何版本的Windows都有类似于Linux的机制,可以调整系统时钟速度,以便慢慢使时钟与外部时钟源同步吗?这样的事情会影响定时器,还是只影响挂钟定时器?
Windows Timestamp Project符合您的要求。据我所知,它只影响挂钟计时器。