Edd*_*die 20 java windows time datetime
出于诊断目的,我希望能够在长时间运行的服务器应用程序中检测系统时钟的变化.由于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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
与Thread.sleep()时间不一致以及中断的不准确应该与定时器漂移完全无关.
这两个Java"系统"调用都用作测量 - 一个用于测量挂钟时间的差异,另一个用于测量绝对间隔,因此当实时时钟未被更改时,这些值应该更改在非常接近相同的速度,对吧?这是Java中的错误或弱点还是失败?操作系统或硬件中是否存在阻止Java更准确的内容?
我完全期望这些独立测量之间存在一些漂移和抖动(**),但我预计每天漂移不到一分钟.每秒1毫秒的漂移,如果是单调的,几乎是90秒!我观察到的最坏情况可能是十倍.每次我运行这个程序时,我都会看到第一次测量的偏差.到目前为止,我还没有运行该程序超过30分钟.
由于抖动,我希望在打印的值中看到一些小的随机性,但在程序的几乎所有运行中,我看到差异的稳定增加,通常高达每秒3毫秒的增加,并且远远超过每秒3毫秒. .
任何版本的Windows都有类似于Linux的机制,可以调整系统时钟速度,以便慢慢使时钟与外部时钟源同步吗?这样的事情会影响定时器,还是只影响挂钟定时器?
(*)我理解在一些架构上,System.nanoTime()必然会使用相同的机制System.currentTimeMillis().我也相信,假设任何现代Windows服务器都不是这样的硬件架构是公平的.这是一个不好的假设吗?
(**)当然,由于在大多数系统上其粒度不是1毫秒,System.currentTimeMillis()因此通常会有更大的抖动System.nanoTime().
Qua*_*nic 11
您可能会发现有关JVM计时器的Sun/Oracle博客文章很有用.
以下是该文章中有关Windows下JVM计时器的几个段落:
System.currentTimeMillis()使用该GetSystemTimeAsFileTime方法实现,该方法基本上只读取Windows维护的低分辨率时间值.根据报告的信息,读取这个全局变量自然非常快 - 大约6个周期.无论定时器中断如何编程,这个时间值都以恒定速率更新 - 取决于平台,这将是10ms或15ms(此值似乎与默认中断周期相关).
System.nanoTime()使用QueryPerformanceCounter/QueryPerformanceFrequencyAPI实现(如果可用,否则返回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频率的值(模数可能有效的任何速度限制或电源管理.)
我不确定这实际上有多大帮助.但这是Windows/Intel/AMD/Java世界中一个积极变化的领域.在几个(至少10年)内,对准确和精确的时间测量的需求已经很明显.英特尔和AMD都通过改变TSC的工作方式做出了回应.两家公司现在都有一种叫做Invariant-TSC和/或Constant-TSC的东西.
检查CPU内核的rdtsc精度.引用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.
"为了测量延迟,我们采用三级流水线并生成低于饱和度的事件.这是通过在注入下一个事件并重复注入5000万次之前等待1微秒来实现的.要达到这个精度水平,有必要使用来自CPU的时间戳计数器.我们选择具有不变TSC的CPU,因为较旧的处理器由于省电和睡眠状态而受到频率变化的影响.Intel Nehalem和后来的处理器使用不变的TSC,可以通过运行的最新Oracle JVM访问Ubuntu 11.04.此测试没有使用CPU绑定"
请注意,"Disruptor"的作者与处理Azul和其他JVM的人有密切联系.
另请参阅"幕后的Java飞行记录".本演示文稿提到了新的不变TSC指令.