天天看點

JVM源碼分析之System.currentTimeMillis及nanoTime原理詳解

上周<code>@望陶</code>問了我一個現象很詭異的問題,說jdk7和jdk8下的<code>system.nanotime()</code>輸出完全不一樣,而且差距還非常大,是不是兩個版本裡的實作不一樣,之前我也沒注意過這個細節,覺得非常奇怪,于是自己也在本地mac機器上馬上測試了一下,得到如下輸出:

還真不一樣,于是我再到linux下跑了一把,發現兩個版本下的值基本上差不多的,也就是主要是mac下的實作可能不一樣

于是我又調用<code>system.currenttimemillis()</code>,發現其輸出結果和<code>system.nanotime()</code>也完全不是1000000倍的比例

另外<code>system.nanotime()</code>輸出的到底是什麼東西,這個數字好奇怪

這三個小細節平時沒有留意,好奇心作祟,于是馬上想一查究竟

再列下主要想理清楚的三個問題

在mac下發現<code>system.nanotime()</code>在jdk7和jdk8下輸出的值怎麼完全不一樣

<code>system.nanotime()</code>的值很奇怪,究竟是怎麼算出來的

<code>system.currenttimemillis()</code>為何不是<code>system.nanotime()</code>的1000000倍

在mac下,首先看jdk7的nanotime實作

再來看jdk8下的實作

果然發現jdk8下多了一個<code>__apple__</code>宏下定義的實作,和jdk7及之前的版本的實作是不一樣的,不過其他bsd系統是一樣的,隻是macos有點不一樣,因為平時咱們主要使用的環境還是linux為主,是以對于macos下具體異同就不做過多解釋了,有興趣的自己去研究一下。

在linux下jdk7和jdk8的實作都是一樣的

而<code>linux::supports_monotonic_clock</code>決定了走哪個具體的分支

<code>_clock_gettime</code>的定義在

說白了,其實就是看librt.so.1或者librt.so中是否定義了<code>clock_gettime</code>函數,如果定義了,就直接調用這個函數來擷取時間,注意下上面的傳給<code>clock_gettime</code>的一個參數是<code>clock_monotonic</code>,至于這個參數的作用後面會說,這個函數在glibc中有定義

而對應的宏<code>sysdep_gettime</code>定義如下:

最終是調用的clock_gettime系統調用:

而我們jvm裡取納秒數時傳入的是clock_monotonic這個參數,是以會調用如下的方法

上面的<code>wall_to_monotonic</code>的<code>tv_sec</code>以及<code>tv_nsec</code>都是負數,在系統啟動初始化的時候設定,記錄了啟動的時間

是以nanotime其實算出來的是一個相對的時間,相對于系統啟動的時候的時間

我們其實可以寫一個簡單的例子從側面來驗證currenttimemillis傳回的到底是什麼值

你将看到輸出結果會是兩個一樣的值,這說明了什麼?另外<code>new date(0).gettime()</code>其實就是<code>1970/01/01 08:00:00</code>,而<code>new date().gettime()</code>是傳回的目前時間,兩個日期一減,其實就是目前時間距離<code>1970/01/01 08:00:00</code>有多少毫秒,而<code>system.currenttimemillis()</code>傳回的正好是這個值,也就是說<code>system.currenttimemillis()</code>就是傳回的目前時間距離<code>1970/01/01 08:00:00</code>的毫秒數。

就實作上來說,currenttimemillis其實是通過<code>gettimeofday</code>來實作的

至此應該大家也清楚了,為什麼currenttimemillis傳回的值并不是nanotime傳回的值的1000000倍左右了,因為兩個值的參照不一樣,是以沒有可比性