Java 为什么热点编译日志时间与ManagementFactory.getRuntimeMXBean().getUptime()不同?

Java 为什么热点编译日志时间与ManagementFactory.getRuntimeMXBean().getUptime()不同?,java,jvm,benchmarking,jvm-hotspot,Java,Jvm,Benchmarking,Jvm Hotspot,当我用参数启动JVM时 -XX:+PrintCompilation 输出如下: 60 1 java.lang.String::hashCode (55 bytes) 74 2 sun.nio.cs.UTF_8$Encoder::encode (361 bytes) [62:log from Java code] 103 5 benchmark.AbstractBenchmarkST::benchma

当我用参数启动JVM时

-XX:+PrintCompilation
输出如下:

 60    1             java.lang.String::hashCode (55 bytes)
 74    2             sun.nio.cs.UTF_8$Encoder::encode (361 bytes)
[62:log from Java code]
103    5             benchmark.AbstractBenchmarkST::benchmark (82 bytes)
[62:log from Java code]
第一列是打印日志时的时间戳(毫秒),我想将此时间戳与
ManagementFactory.getRuntimeMXBean().getUptime()返回的值进行比较:

但我的结果是这样的:

 60    1             java.lang.String::hashCode (55 bytes)
 74    2             sun.nio.cs.UTF_8$Encoder::encode (361 bytes)
[62:log from Java code]
103    5             benchmark.AbstractBenchmarkST::benchmark (82 bytes)
[62:log from Java code]

它们之间似乎相差约40毫秒,这使得两个时间戳无法相比。有什么办法解决这个问题吗?

简短回答

热点JIT编译日志第一列中打印的时间(使用“-XX:+PrintCompilation”参数启动JVM时)的持续时间恒定,大于ManagementFactory.getRuntimeMXBean().getUptime()返回的时间(假定getUptime与打印编译日志的时间大致相同)

这至少适用于在Windows 7下运行的JDK 7,并且可以通过使用“-XX:+printcomilation”执行以下代码来轻松验证:

结果应该如下所示:

 60    1             java.lang.String::hashCode (55 bytes)
 74    2             sun.nio.cs.UTF_8$Encoder::encode (361 bytes)
[62:log from Java code]
103    5             benchmark.AbstractBenchmarkST::benchmark (82 bytes)
[62:log from Java code]
JVM正常运行时间:43

尽管ManagementFactory.getRuntimeMXBean().getUptime()是在打印的JIT编译之后调用的,但返回的时间似乎指向了以前的调用

似乎它们之间有40毫秒左右的差异,这种差异使其无法比拟。你知道怎么处理吗

由于时差是恒定的,并且在运行JVM的过程中不会发生变化,所以只要考虑到时差,就应该能够比较时间

长答案

“-XX:+printcomilation”JVM参数几乎没有文档记录,人们只能猜测,第一列表示与JVM启动相关的时间戳。但是,如果看一下HotSpot编译器的源代码,就会发现PrintCompilation打印的时间和ManagementFactory.getRuntimeMXBean().getStartTime()返回的时间引用了两个完全不同的时间戳,这两个时间戳都是在JVM启动期间初始化的

在调用以下对象时打印编译日志:

st->time_stamp()在ostream.cpp中实现,并引用使用os::Expressed_counter()返回的时间初始化的:

:

:

在以下过程中,通过调用os::init()依次初始化os::eaperd_counter():

另一方面,java方法ManagementFactory.getRuntimeMXBean().getStartTime()在以下情况中引用本机方法:

它在中实现,并从JmmInterface常量JMM_JVM_INIT_DONE_time_MS返回时间:

:

在已调用os::init()的一段时间后,在JVM启动期间初始化:

jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
    [...]
    os::init();
    [...]
    // record VM initialization completion time
    Management::record_vm_init_completed();
    [...]
}

因此,JIT编译日志打印的时间与ManagementFactory.getRuntimeMXBean().getStartTime()返回的时间不同。

哪段代码写入了两次?
 void outputStream::stamp() {
     if (! _stamp.is_updated()) {
         _stamp.update(); // start at 0 on first call to stamp()
     }
     [...]
 }
 void TimeStamp::update() {
     update_to(os::elapsed_counter());
 }
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
    [...]
    os::init();
    [...]
}
public native long getStartupTime();
  case JMM_JVM_INIT_DONE_TIME_MS:
       return Management::vm_init_done_time();
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
    [...]
    os::init();
    [...]
    // record VM initialization completion time
    Management::record_vm_init_completed();
    [...]
}