System.nanoTime()仅提供单调递增纳秒值,适合测间隔而非时间戳;其差值为0因分辨率低、JIT优化或调用开销大;不可与currentTimeMillis()混用;微基准测试需预热、避GC、保内联、多次测量。

System.nanoTime() 本身不提供“绝对时间”,它只返回一个单调递增的纳秒级计时值,适合测间隔,不适合做时间戳。它的精度和稳定性取决于底层操作系统与硬件,不是所有平台都能真正达到纳秒级分辨率。
为什么 System.nanoTime() 测出来的时间差有时为 0
常见现象:连续两次调用 System.nanoTime() 相减得 0,尤其在空循环或极短操作中。
- 底层计时器分辨率有限(如 Windows 上常为 10–15ms,Linux 可能是 1–10ns,但受 CPU 频率调节、虚拟化影响)
- JIT 编译可能优化掉“无副作用”的代码段,导致实际没执行
- 单次调用开销本身就可能接近或超过计时器最小可分辨间隔
- 建议至少重复测量 100 次以上取平均,并用
Thread.onSpinWait()或简单 volatile 读写防止过度优化
System.nanoTime() 和 System.currentTimeMillis() 能否混用
绝对不能。两者基准不同、更新机制不同、用途完全不同。
-
System.currentTimeMillis()返回自 1970-01-01 UTC 的毫秒数,受系统时钟调整(NTP、手动修改)影响,可能回跳或跳跃 -
System.nanoTime()基于某个未公开起点(如 JVM 启动时刻),只保证单调性,不映射到真实时间 - 拿
System.nanoTime()减去一个System.currentTimeMillis()值毫无意义,结果单位错乱且语义失效 - 日志打点若需“带时间戳的耗时”,应分别记录
System.currentTimeMillis()(用于排序/对齐)和System.nanoTime()(用于精确差值)
在微基准测试中如何避免 System.nanoTime() 的典型误用
JMH 是首选,但若手写简易 benchmark,必须绕过几个关键陷阱:
立即学习“Java免费学习笔记(深入)”;
- 预热不足:JVM 需要数万次执行才能完成 JIT 编译,首次测得的值严重失真
- 没有隔离 GC 干扰:一次
System.gc()可能打断测量;建议用-XX:+PrintGCDetails观察是否发生 GC - 忽略方法内联限制:被测方法太长或含分支/异常,JIT 可能拒绝内联,引入额外 call 开销
- 错误地把多次调用合并成一次测量:例如
for (int i = 0; i 然后只包一层nanoTime()—— 这测的是整体耗时,无法归一化到单次,应改为long start = System.nanoTime(); for (...) { work(); } long end = System.nanoTime();再除以 N
最易被忽略的一点:System.nanoTime() 在某些容器环境(尤其是启用了 CPU quota 的 Docker)下会出现非线性漂移,表现为相同代码在不同运行周期测出差异达几十纳秒——这不是 Java 的问题,而是 Linux cfs_bandwidth 的调度抖动所致,此时应考虑用 CLOCK_MONOTONIC_RAW(需 JNI)或接受误差范围。










