daw*_*tar 7 java benchmarking jvm jvm-hotspot
当我用参数启动JVM时
-XX:+PrintCompilation
Run Code Online (Sandbox Code Playgroud)
输出是这样的:
60 1 java.lang.String::hashCode (55 bytes)
74 2 sun.nio.cs.UTF_8$Encoder::encode (361 bytes)
Run Code Online (Sandbox Code Playgroud)
第一列是以毫秒为单位的时间戳,当打印日志时,我想将此时间戳与返回的值进行比较ManagementFactory.getRuntimeMXBean().getUptime():
long jvmUpTime = ManagementFactory.getRuntimeMXBean().getUptime();
Run Code Online (Sandbox Code Playgroud)
要么
long jvmStartTime = ManagementFactory.getRuntimeMXBean().getStartTime();
Run Code Online (Sandbox Code Playgroud)
但我的结果是这样的:
[62:log from Java code]
103 5 benchmark.AbstractBenchmarkST::benchmark (82 bytes)
[62:log from Java code]
Run Code Online (Sandbox Code Playgroud)
似乎它们之间有大约40毫秒的差异,这使得两个时间戳无法比拟.任何想法如何处理这个?
简答
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());
}
Run Code Online (Sandbox Code Playgroud)
结果应该是这样的:
Run Code Online (Sandbox Code Playgroud)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
尽管 ManagementFactory.getRuntimeMXBean().getUptime() 在打印的 JIT 编译之后被调用,但返回的时间似乎指向更早的调用。
看起来他们有大约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
[...]
}
Run Code Online (Sandbox Code Playgroud)
st->time_stamp() 在 ostream.cpp 中实现,并引用使用 os::elapsed_counter() 返回的时间初始化的TimeStamp :
void outputStream::stamp() {
if (! _stamp.is_updated()) {
_stamp.update(); // start at 0 on first call to stamp()
}
[...]
}
Run Code Online (Sandbox Code Playgroud)
void TimeStamp::update() {
update_to(os::elapsed_counter());
}
Run Code Online (Sandbox Code Playgroud)
os::elapsed_counter() 在JVM 启动期间通过调用 os::init() 依次初始化:
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
[...]
os::init();
[...]
}
Run Code Online (Sandbox Code Playgroud)
另一方面,java 方法 ManagementFactory.getRuntimeMXBean().getStartTime() 引用VMManagementImpl.java中的本机方法:
public native long getStartupTime();
Run Code Online (Sandbox Code Playgroud)
它在VMManagementImpl.c中实现,并从 JmmInterface 常量 JMM_JVM_INIT_DONE_TIME_MS 返回时间:
case JMM_JVM_INIT_DONE_TIME_MS:
return Management::vm_init_done_time();
Run Code Online (Sandbox Code Playgroud)
在 os::init() 被调用后相当长一段时间内,它在 JVM 启动期间被初始化:
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
[...]
os::init();
[...]
// record VM initialization completion time
Management::record_vm_init_completed();
[...]
}
Run Code Online (Sandbox Code Playgroud)
因此,JIT 编译日志打印的时间与 ManagementFactory.getRuntimeMXBean().getStartTime() 返回的时间不同。
| 归档时间: |
|
| 查看次数: |
360 次 |
| 最近记录: |