Java中不要使用System.currentTimeMillis()除錯測試延遲時間

banq發表於2021-06-14

System.currentTimeMillis()是所謂掛鐘概念:當前時間與 UTC 時間 1970 年 1 月 1 日午夜之間的差值(以毫秒為單位)。它只是告訴您當前系統時間是多少。並且使用它會導致很多問題:
 

1. 粒度
System.currentTimeMillis()以毫秒為單位返回當前時間,但值的粒度取決於底層作業系統,並且可能大於基本單位。例如,許多作業系統以幾十毫秒為單位測量時間。
假設您的作業系統以 100 毫秒為單位測量時間。在這種情況下,沒有辦法衡量需要比這更精確的任何東西。因此,如果某個操作需要 20 到 80 毫秒之間的某個時間,則您的測量結果為 0 或 100 毫秒(此精度可能會更差,例如:1 秒),更不用說測量亞毫秒級操作(微基準、奈米基準)。
 

2. 時間均勻性
您可能聽說過地球自轉存在不規則性(它以複雜的方式減慢和加速)。此外,地球自轉會長期放緩。有一個標準,稱為UT1(世界時的一個版本),它基於天文觀測和地球自轉。因此,UT1 並不總是均勻流動。因此,如果您的時間源基於此,則您有一個不均勻的時間源,它可以加速和減速,從而導致測量錯誤(請參閱:javadoc ofDate)。 System.currentTimeMillis()使用 UTC 而不是 UT1 所以它沒有這個確切的問題。
 

3. 閏秒

UTC是透過精確的原子鐘測量的,而UT1是基於天文觀測的(參見上一節)。為了使兩者保持一定程度的接近(<0.9 秒),UTC 偶爾會調整一秒。這種調整稱為閏秒。由於氣候和地質事件會影響地球的自轉,UTC 閏秒是不規則且不可預測的。這些調整可能會導致測量錯誤,因為在測量過程中時鐘可能會發生變化(請參閱:javadoc ofDate)。
 

4. 同步系統時鐘
當時鐘不以相同的速率執行時,可能會發生時鐘漂移(它們不同步)。為了讓它們彼此接近,時鐘同步是必要的(參見:NTP)。由於您的系統時鐘精度與原子鐘的精度相差甚遠,因此需要定期調整到UTC的幾毫秒內。這可能意味著時間跳躍或使時鐘走得更快或更慢,以便它會向 UTC 漂移。這些調整也會導致測量誤差。

5. 夏令時
如果夏令時在此類測量中引起問題,則需要真正搞砸一些事情,但如果您需要處理時間,則這是一個非常常見的問題。我見過一次服務每年崩潰兩次,你能猜到為什麼嗎?
  

解決方案:System.nanoTime()
解決方案非常簡單:不要使用“掛鐘”。在 Java 中:System.nanoTime()您可以訪問專為測量經過時間而設計的高解析度時間源(納秒),它與系統時鐘或任何“掛鐘”無關,請參閱 的javadocSystem.nanoTime()。所以你可以這樣做:

Response handleRequest(Request request) {
    long start = System.nanoTime();
    try {
        return doSomething(request);
    }
    finally {
        record(System.nanoTime() - start);
    }
}


如果在做其他效能測試,建議:
  • 如果你在做 (nano/micro/milli/macro) 基準測試:JMH
  • 如果您想為您的應用程式收集指標:Micrometer
  • 如果您想對您的 Web 服務進行效能測試:Gatling


 

相關文章