上周@望陶
问了我一个现象很诡异的问题,说JDK7和JDK8下的System.nanoTime()
输出彻底不同,并且差距还很是大,是否是两个版本里的实现不同,以前我也没注意过这个细节,以为很是奇怪,因而本身也在本地mac机器上立刻测试了一下,获得以下输出:java
~/Documents/workspace/Test/src ᐅ /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java NanosTest 1480265318432558000 ~/Documents/workspace/Test/src ᐅ /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/java NanosTest 1188453233877
~/Documents/workspace/Test/src ᐅ /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java NanosTest 1480265318432558000 ~/Documents/workspace/Test/src ᐅ /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/java NanosTest 1188453233877
还真不同,因而我再到linux下跑了一把,发现两个版本下的值基本上差很少的,也就是主要是mac下的实现可能不同linux
因而我又调用System.currentTimeMillis()
,发现其输出结果和System.nanoTime()
也彻底不是1000000倍的比例macos
~/Documents/workspace/Test/src ᐅ /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/java NanosTest 1563115443175 1480265707257
~/Documents/workspace/Test/src ᐅ /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/java NanosTest 1563115443175 1480265707257
另外System.nanoTime()
输出的究竟是什么东西,这个数字好奇怪oracle
这三个小细节平时没有留意,好奇心做祟,因而立刻想一查究竟函数
再列下主要想理清楚的三个问题oop
System.nanoTime()
在JDK7和JDK8下输出的值怎么彻底不同System.nanoTime()
的值很奇怪,到底是怎么算出来的System.currentTimeMillis()
为什么不是System.nanoTime()
的1000000倍在mac下,首先看JDK7的nanoTime实现测试
jlong os::javaTimeNanos() { if (Bsd::supports_monotonic_clock()) { struct timespec tp; int status = Bsd::clock_gettime(CLOCK_MONOTONIC, &tp); assert(status == 0, "gettime error"); jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec); return result; } else { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "bsd error"); jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec); return 1000 * usecs; } }
jlong os::javaTimeNanos() { if (Bsd::supports_monotonic_clock()) { struct timespec tp; int status = Bsd::clock_gettime(CLOCK_MONOTONIC, &tp); assert(status == 0, "gettime error"); jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec); return result; } else { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "bsd error"); jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec); return 1000 * usecs; } }
再来看JDK8下的实现ui
#ifdef __APPLE__ jlong os::javaTimeNanos() { const uint64_t tm = mach_absolute_time(); const uint64_t now = (tm * Bsd::_timebase_info.numer) / Bsd::_timebase_info.denom; const uint64_t prev = Bsd::_max_abstime; if (now <= prev) { return prev; // same or retrograde time; } const uint64_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&Bsd::_max_abstime, prev); assert(obsv >= prev, "invariant"); // Monotonicity // If the CAS succeeded then we're done and return "now". // If the CAS failed and the observed value "obsv" is >= now then // we should return "obsv". If the CAS failed and now > obsv > prv then // some other thread raced this thread and installed a new value, in which case // we could either (a) retry the entire operation, (b) retry trying to install now // or (c) just return obsv. We use (c). No loop is required although in some cases // we might discard a higher "now" value in deference to a slightly lower but freshly // installed obsv value. That's entirely benign -- it admits no new orderings compared // to (a) or (b) -- and greatly reduces coherence traffic. // We might also condition (c) on the magnitude of the delta between obsv and now. // Avoiding excessive CAS operations to hot RW locations is critical. // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate return (prev == obsv) ? now : obsv; } #else // __APPLE__
#ifdef __APPLE__ jlong os::javaTimeNanos() { const uint64_t tm = mach_absolute_time(); const uint64_t now = (tm * Bsd::_timebase_info.numer) / Bsd::_timebase_info.denom; const uint64_t prev = Bsd::_max_abstime; if (now <= prev) { return prev; // same or retrograde time; } const uint64_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&Bsd::_max_abstime, prev); assert(obsv >= prev, "invariant"); // Monotonicity // If the CAS succeeded then we're done and return "now". // If the CAS failed and the observed value "obsv" is >= now then // we should return "obsv". If the CAS failed and now > obsv > prv then // some other thread raced this thread and installed a new value, in which case // we could either (a) retry the entire operation, (b) retry trying to install now // or (c) just return obsv. We use (c). No loop is required although in some cases // we might discard a higher "now" value in deference to a slightly lower but freshly // installed obsv value. That's entirely benign -- it admits no new orderings compared // to (a) or (b) -- and greatly reduces coherence traffic. // We might also condition (c) on the magnitude of the delta between obsv and now. // Avoiding excessive CAS operations to hot RW locations is critical. // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate return (prev == obsv) ? now : obsv; } #else // __APPLE__
果真发现JDK8下多了一个__APPLE__
宏下定义的实现,和JDK7及以前的版本的实现是不同的,不过其余BSD系统是同样的,只是macos有点不同,由于平时我们主要使用的环境仍是Linux为主,所以对于macos下具体异同就不作过多解释了,有兴趣的本身去研究一下。this
在linux下JDK7和JDK8的实现都是同样的spa
jlong os::javaTimeNanos() { if (Linux::supports_monotonic_clock()) { struct timespec tp; int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp); assert(status == 0, "gettime error"); jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec); return result; } else { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "linux error"); jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec); return 1000 * usecs; } }
jlong os::javaTimeNanos() { if (Linux::supports_monotonic_clock()) { struct timespec tp; int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp); assert(status == 0, "gettime error"); jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec); return result; } else { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "linux error"); jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec); return 1000 * usecs; } }
而Linux::supports_monotonic_clock
决定了走哪一个具体的分支
static inline bool supports_monotonic_clock() { return _clock_gettime != NULL; }
static inline bool supports_monotonic_clock() { return _clock_gettime != NULL; }
_clock_gettime
的定义在
void os::Linux::clock_init() { // we do dlopen's in this particular order due to bug in linux // dynamical loader (see 6348968) leading to crash on exit void* handle = dlopen("librt.so.1", RTLD_LAZY); if (handle == NULL) { handle = dlopen("librt.so", RTLD_LAZY); } if (handle) { int (*clock_getres_func)(clockid_t, struct timespec*) = (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_getres"); int (*clock_gettime_func)(clockid_t, struct timespec*) = (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_gettime"); if (clock_getres_func && clock_gettime_func) { // See if monotonic clock is supported by the kernel. Note that some // early implementations simply return kernel jiffies (updated every // 1/100 or 1/1000 second). It would be bad to use such a low res clock // for nano time (though the monotonic property is still nice to have). // It's fixed in newer kernels, however clock_getres() still returns // 1/HZ. We check if clock_getres() works, but will ignore its reported // resolution for now. Hopefully as people move to new kernels, this // won't be a problem. struct timespec res; struct timespec tp; if (clock_getres_func (CLOCK_MONOTONIC, &res) == 0 && clock_gettime_func(CLOCK_MONOTONIC, &tp) == 0) { // yes, monotonic clock is supported _clock_gettime = clock_gettime_func; return; } else { // close librt if there is no monotonic clock dlclose(handle); } } } warning("No monotonic clock was available - timed services may " \ "be adversely affected if the time-of-day clock changes"); }
void os::Linux::clock_init() { // we do dlopen's in this particular order due to bug in linux // dynamical loader (see 6348968) leading to crash on exit void* handle = dlopen("librt.so.1", RTLD_LAZY); if (handle == NULL) { handle = dlopen("librt.so", RTLD_LAZY); } if (handle) { int (*clock_getres_func)(clockid_t, struct timespec*) = (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_getres"); int (*clock_gettime_func)(clockid_t, struct timespec*) = (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_gettime"); if (clock_getres_func && clock_gettime_func) { // See if monotonic clock is supported by the kernel. Note that some // early implementations simply return kernel jiffies (updated every // 1/100 or 1/1000 second). It would be bad to use such a low res clock // for nano time (though the monotonic property is still nice to have). // It's fixed in newer kernels, however clock_getres() still returns // 1/HZ. We check if clock_getres() works, but will ignore its reported // resolution for now. Hopefully as people move to new kernels, this // won't be a problem. struct timespec res; struct timespec tp; if (clock_getres_func (CLOCK_MONOTONIC, &res) == 0 && clock_gettime_func(CLOCK_MONOTONIC, &tp) == 0) { // yes, monotonic clock is supported _clock_gettime = clock_gettime_func; return; } else { // close librt if there is no monotonic clock dlclose(handle); } } } warning("No monotonic clock was available - timed services may " \ "be adversely affected if the time-of-day clock changes"); }
说白了,其实就是看librt.so.1或者librt.so中是否认义了clock_gettime
函数,若是定义了,就直接调用这个函数来获取时间,注意下上面的传给clock_gettime
的一个参数是CLOCK_MONOTONIC
,至于这个参数的做用后面会说,这个函数在glibc中有定义
/* Get current value of CLOCK and store it in TP. */ int __clock_gettime (clockid_t clock_id, struct timespec *tp) { int retval = -1; switch (clock_id) { #ifdef SYSDEP_GETTIME SYSDEP_GETTIME; #endif #ifndef HANDLED_REALTIME case CLOCK_REALTIME: { struct timeval tv; retval = gettimeofday (&tv, NULL); if (retval == 0) TIMEVAL_TO_TIMESPEC (&tv, tp); } break; #endif default: #ifdef SYSDEP_GETTIME_CPU SYSDEP_GETTIME_CPU (clock_id, tp); #endif #if HP_TIMING_AVAIL if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) == CLOCK_THREAD_CPUTIME_ID) retval = hp_timing_gettime (clock_id, tp); else #endif __set_errno (EINVAL); break; #if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME case CLOCK_PROCESS_CPUTIME_ID: retval = hp_timing_gettime (clock_id, tp); break; #endif } return retval; } weak_alias (__clock_gettime, clock_gettime) libc_hidden_def (__clock_gettime)
/* Get current value of CLOCK and store it in TP. */ int __clock_gettime (clockid_t clock_id, struct timespec *tp) { int retval = -1; switch (clock_id) { #ifdef SYSDEP_GETTIME SYSDEP_GETTIME; #endif #ifndef HANDLED_REALTIME case CLOCK_REALTIME: { struct timeval tv; retval = gettimeofday (&tv, NULL); if (retval == 0) TIMEVAL_TO_TIMESPEC (&tv, tp); } break; #endif default: #ifdef SYSDEP_GETTIME_CPU SYSDEP_GETTIME_CPU (clock_id, tp); #endif #if HP_TIMING_AVAIL if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) == CLOCK_THREAD_CPUTIME_ID) retval = hp_timing_gettime (clock_id, tp); else #endif __set_errno (EINVAL); break; #if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME case CLOCK_PROCESS_CPUTIME_ID: retval = hp_timing_gettime (clock_id, tp); break; #endif } return retval; } weak_alias (__clock_gettime, clock_gettime) libc_hidden_def (__clock_gettime)
而对应的宏SYSDEP_GETTIME
定义以下:
#define SYSDEP_GETTIME \ SYSDEP_GETTIME_CPUTIME; \ case CLOCK_REALTIME: \ case CLOCK_MONOTONIC: \ retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp); \ break /* We handled the REALTIME clock here. */ #define HANDLED_REALTIME 1 #define HANDLED_CPUTIME 1 #define SYSDEP_GETTIME_CPU(clock_id, tp) \ retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp); \ break #define SYSDEP_GETTIME_CPUTIME /* Default catches them too. */
#define SYSDEP_GETTIME \ SYSDEP_GETTIME_CPUTIME; \ case CLOCK_REALTIME: \ case CLOCK_MONOTONIC: \ retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp); \ break /* We handled the REALTIME clock here. */ #define HANDLED_REALTIME 1 #define HANDLED_CPUTIME 1 #define SYSDEP_GETTIME_CPU(clock_id, tp) \ retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp); \ break #define SYSDEP_GETTIME_CPUTIME /* Default catches them too. */
最终是调用的clock_gettime系统调用:
int clock_gettime(clockid_t, struct timespec *) __attribute__((weak, alias("__vdso_clock_gettime"))); notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) { if (likely(gtod->sysctl_enabled)) switch (clock) { case CLOCK_REALTIME: if (likely(gtod->clock.vread)) return do_realtime(ts); break; case CLOCK_MONOTONIC: if (likely(gtod->clock.vread)) return do_monotonic(ts); break; case CLOCK_REALTIME_COARSE: return do_realtime_coarse(ts); case CLOCK_MONOTONIC_COARSE: return do_monotonic_coarse(ts); } return vdso_fallback_gettime(clock, ts); }
int clock_gettime(clockid_t, struct timespec *) __attribute__((weak, alias("__vdso_clock_gettime"))); notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) { if (likely(gtod->sysctl_enabled)) switch (clock) { case CLOCK_REALTIME: if (likely(gtod->clock.vread)) return do_realtime(ts); break; case CLOCK_MONOTONIC: if (likely(gtod->clock.vread)) return do_monotonic(ts); break; case CLOCK_REALTIME_COARSE: return do_realtime_coarse(ts); case CLOCK_MONOTONIC_COARSE: return do_monotonic_coarse(ts); } return vdso_fallback_gettime(clock, ts); }
而咱们JVM里取纳秒数时传入的是CLOCK_MONOTONIC这个参数,所以会调用以下的方法
notrace static noinline int do_monotonic(struct timespec *ts) { unsigned long seq, ns, secs; do { seq = read_seqbegin(>od->lock); secs = gtod->wall_time_sec; ns = gtod->wall_time_nsec + vgetns(); secs += gtod->wall_to_monotonic.tv_sec; ns += gtod->wall_to_monotonic.tv_nsec; } while (unlikely(read_seqretry(>od->lock, seq))); vset_normalized_timespec(ts, secs, ns); return 0; }
notrace static noinline int do_monotonic(struct timespec *ts) { unsigned long seq, ns, secs; do { seq = read_seqbegin(>od->lock); secs = gtod->wall_time_sec; ns = gtod->wall_time_nsec + vgetns(); secs += gtod->wall_to_monotonic.tv_sec; ns += gtod->wall_to_monotonic.tv_nsec; } while (unlikely(read_seqretry(>od->lock, seq))); vset_normalized_timespec(ts, secs, ns); return 0; }
上面的wall_to_monotonic
的tv_sec
以及tv_nsec
都是负数,在系统启动初始化的时候设置,记录了启动的时间
void __init timekeeping_init(void) { struct clocksource *clock; unsigned long flags; struct timespec now, boot; read_persistent_clock(&now); read_boot_clock(&boot); write_seqlock_irqsave(&xtime_lock, flags); ntp_init(); clock = clocksource_default_clock(); if (clock->enable) clock->enable(clock); timekeeper_setup_internals(clock); xtime.tv_sec = now.tv_sec; xtime.tv_nsec = now.tv_nsec; raw_time.tv_sec = 0; raw_time.tv_nsec = 0; if (boot.tv_sec == 0 && boot.tv_nsec == 0) { boot.tv_sec = xtime.tv_sec; boot.tv_nsec = xtime.tv_nsec; } set_normalized_timespec(&wall_to_monotonic, -boot.tv_sec, -boot.tv_nsec); total_sleep_time.tv_sec = 0; total_sleep_time.tv_nsec = 0; write_sequnlock_irqrestore(&xtime_lock, flags); }
void __init timekeeping_init(void) { struct clocksource *clock; unsigned long flags; struct timespec now, boot; read_persistent_clock(&now); read_boot_clock(&boot); write_seqlock_irqsave(&xtime_lock, flags); ntp_init(); clock = clocksource_default_clock(); if (clock->enable) clock->enable(clock); timekeeper_setup_internals(clock); xtime.tv_sec = now.tv_sec; xtime.tv_nsec = now.tv_nsec; raw_time.tv_sec = 0; raw_time.tv_nsec = 0; if (boot.tv_sec == 0 && boot.tv_nsec == 0) { boot.tv_sec = xtime.tv_sec; boot.tv_nsec = xtime.tv_nsec; } set_normalized_timespec(&wall_to_monotonic, -boot.tv_sec, -boot.tv_nsec); total_sleep_time.tv_sec = 0; total_sleep_time.tv_nsec = 0; write_sequnlock_irqrestore(&xtime_lock, flags); }
所以nanoTime其实算出来的是一个相对的时间,相对于系统启动的时候的时间
咱们其实能够写一个简单的例子从侧面来验证currentTimeMillis返回的究竟是什么值
public static void main(String args[]) { System.out.println(new Date().getTime()-new Date(0).getTime()); System.out.println(System.currentTimeMillis()); }
public static void main(String args[]) { System.out.println(new Date().getTime()-new Date(0).getTime()); System.out.println(System.currentTimeMillis()); }
你将看到输出结果会是两个同样的值,这说明了什么?另外new Date(0).getTime()
其实就是1970/01/01 08:00:00
,而new Date().getTime()
是返回的当前时间,两个日期一减,其实就是当前时间距离1970/01/01 08:00:00
有多少毫秒,而System.currentTimeMillis()
返回的正好是这个值,也就是说System.currentTimeMillis()
就是返回的当前时间距离1970/01/01 08:00:00
的毫秒数。
就实现上来讲,currentTimeMillis实际上是经过gettimeofday
来实现的
jlong os::javaTimeMillis() { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "linux error"); return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000); }
jlong os::javaTimeMillis() { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "linux error"); return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000); }
至此应该你们也清楚了,为何currentTimeMillis返回的值并非nanoTime返回的值的1000000倍左右了,由于两个值的参照不同,因此没有可比性