Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

System.currentTimeMillis()のOS・JavaVMごとの精度

動機

JavaSystem.currentTimeMillis()の説明*1には以下のように書いてあるけど本当? System.currentTimeMillis()System.nanoTime()で試してみた。

たとえば、多くのオペレーティング・システムでは、時間を10ミリ秒の単位で計測します。

方法

以下のプログラムを実行した結果を確認した。OSやVMごとの違いなどもあるかもしれないので、JDK 8/11/17それぞれについて、Windows上・WSL2上のものと、オンラインIDEの結果を取得した。

    public static void main(String args[]) {
        System.out.println(System.getProperty("os.name"));
        System.out.println(System.getProperty("java.vm.name"));
        System.out.println(System.getProperty("java.vendor"));
        System.out.println(System.getProperty("java.version"));

        for (int i=0; i<100; i++) {
            System.out.println(String.format("%19d", System.currentTimeMillis()));
        }
        for (int i=0; i<100; i++) {
            System.out.println(String.format("%19d", System.nanoTime()));
        }
    }

結果

https://docs.google.com/spreadsheets/d/189di6xCjxbALEpCVxdQS8cIA9OXWpjCacx2O3lwKJJo/edit?usp=sharing

考察

  • 「10ミリ秒の単位で計測」とのことだが、どの環境でもSystem.currentTimeMillis()の数値自体は1ミリ秒単位の粒度で取得できている。
    • 間隔については、ときどき数十ミリ秒程度飛ぶことがあり、これはタイムスライス(Windowsの場合15msくらい*2)によるものと思われる。JavaAPIドキュメントにあるのはこのことを言っている?
  • System.nanoTime()の方はOSによって傾向に差が出ている。
    • 粒度は100ns程度のものから1ns単位の数値を返すものまでいろいろ。
    • Windowsの場合、100ns単位での数値を返し、下2桁は常にゼロになっているのが特徴的。
    • WSLの場合、一見して1ns単位の値が取れているように見えるが、下2桁の数値は常に同じなので実質的な粒度は100ns単位と見てよいだろう。
    • オンラインIDEでも、一見して1ns単位の値が取れているように見えるが、しばらくすると下1桁がずっと同じ数値になっていたりすることがある。
  • どのケースもSystem.currentTimeMillis()はループの1回目だけ間隔が広いのは最適化のせいなのかな。でも、System.nanoTime()の方はそんなことないんだよなぁ……