上週@望陶
問了我一個現象很詭異的問題,說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倍左右了,由於兩個值的參照不同,因此沒有可比性