简答
HotSpots JIT 编译日志的第一列中打印的时间(使用“-XX:+PrintCompilation”参数启动 JVM 时)的持续时间恒定大于 ManagementFactory.getRuntimeMXBean().getUptime() 返回的时间(假设 getUptime 在编译日志被打印的同时被调用)。
这对于在 Windows 7 下运行的 JDK 7 来说至少是正确的,并且可以通过使用“-XX:+PrintCompilation”执行以下代码来轻松验证:
public static void main(String[] args) {
System.out.println("JVM uptime: " + ManagementFactory.getRuntimeMXBean().getUptime());
}
结果应如下所示:
77 1 java.lang.String::hashCode (55 bytes)
79 2 java.lang.String::indexOf (70 bytes)
80 3 java.lang.String::charAt (29 bytes)
82 4 java.lang.String::lastIndexOf (52 bytes)
82 5 java.lang.String::equals (81 bytes)
82 6 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
82 7 java.lang.String::indexOf (166 bytes)
85 8 n java.lang.System::arraycopy (native) (static)
JVM 正常运行时间:43
即使在打印 JIT 编译之后调用了 ManagementFactory.getRuntimeMXBean().getUptime(),返回的时间似乎指向更早的调用。
似乎它们有大约 40 毫秒的差异,这种差异使它无法比拟。任何想法如何处理这个?
由于时间差是恒定的,并且在整个运行 JVM 期间不应该改变,所以只要考虑时间差,人们仍然应该能够比较时间。
长答案
“-XX:+PrintCompilation” JVM 参数几乎没有文档记录,只能猜测,第一列表示相对于 JVM 启动的时间戳。如果看一下 HotSpot 编译器的源代码,就会很清楚,PrintCompilation 打印的时间和 ManagementFactory.getRuntimeMXBean().getStartTime() 返回的时间引用了两个完全不同的时间戳,它们都在期间初始化JVM的启动。
在调用CompileTask::print_compilation_impl期间会打印编译日志:
void CompileTask::print_compilation_impl(outputStream* st, Method* method, int compile_id, int comp_level,
bool is_osr_method, int osr_bci, bool is_blocking,
const char* msg, bool short_form) {
if (!short_form) {
st->print("%7d ", (int) st->time_stamp().milliseconds()); // print timestamp
}
st->print("%4d ", compile_id); // print compilation number
[...]
}
st->time_stamp() 在 ostream.cpp 中实现,它指的是一个TimeStamp,它使用 os::elapsed_counter() 返回的时间进行初始化:
ostream.cpp:
void outputStream::stamp() {
if (! _stamp.is_updated()) {
_stamp.update(); // start at 0 on first call to stamp()
}
[...]
}
计时器.cpp:
void TimeStamp::update() {
update_to(os::elapsed_counter());
}
os::elapsed_counter() 在JVM 启动期间依次通过调用 os::init() 来初始化:
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
[...]
os::init();
[...]
}
另一方面,Java 方法 ManagementFactory.getRuntimeMXBean().getStartTime() 指的是VMManagementImpl.java中的本机方法:
public native long getStartupTime();
它在VMManagementImpl.c中实现,并从 JmmInterface 常量 JMM_JVM_INIT_DONE_TIME_MS 返回时间:
管理.cpp:
case JMM_JVM_INIT_DONE_TIME_MS:
return Management::vm_init_done_time();
在 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() 返回的时间不同。