System.nanoTime()更适合测耗时,因其是纳秒级单调计数器,不受系统时钟调整影响,专为测量时间间隔设计;而currentTimeMillis()依赖易变的墙钟时间,毫秒级精度且可能跳变。

为什么 System.nanoTime() 比 System.currentTimeMillis() 更适合测耗时
因为 System.currentTimeMillis() 依赖系统时钟,可能被 NTP 调整、手动修改或存在毫秒级跳变,且最小分辨率通常是毫秒;而 System.nanoTime() 返回的是从某个未指定起点开始的纳秒级单调递增计数器,不受系统时间调整影响,分辨率通常在几纳秒到几十纳秒之间(取决于 OS 和硬件),专为测量间隔设计。
注意:System.nanoTime() 的绝对值无意义,只应做差值使用;也不能用来计算“当前时间”,它不是时间戳。
怎么安全地用 System.nanoTime() 测一段代码的执行时间
最简但易错的方式是直接前后调用两次 System.nanoTime() 取差。但实际中必须考虑 JVM 预热、JIT 编译、GC 干扰等因素——单次测量几乎没参考价值。
- 至少重复运行 1000 次以上,取中位数或平均值(排除 JIT 首次开销)
- 用
Blackhole.consumeCPU()(来自 JMH)或空循环预热,但生产环境简单测试可用Thread.onSpinWait()或多次空跑 - 避免把
System.nanoTime()调用本身计入待测逻辑——它开销约 10–30 纳秒,对微秒级操作有干扰 - 示例(仅示意,非基准测试):
long start = System.nanoTime(); doSomething(); long end = System.nanoTime(); long elapsedNs = end - start;
System.nanoTime() 的常见误用和坑
很多人以为“纳秒级返回 = 纳秒级精度”,其实不然。实际精度受限于底层 CLOCK_MONOTONIC(Linux)或 QueryPerformanceCounter(Windows)实现,以及 JVM 对其封装的开销。
- 差值小于 1000 纳秒(1 微秒)时,结果很可能只是噪声,尤其在容器或虚拟化环境中
- 跨线程调用不安全:不同 CPU 核心的 TSC 可能不同步,
System.nanoTime()在多核下虽已尽力校准,但极短间隔( - 不要用它做超长间隔测量(如 >1 小时):虽然理论上不会溢出(64 位纳秒可存约 292 年),但长期运行中时钟源漂移或系统休眠会导致误差累积
- 日志中打印
System.nanoTime()绝对值毫无意义,曾有人误把它当时间戳存库,导致后续无法解析
什么场景下不该用 System.nanoTime() 测耗时
当你的目标不是“这段逻辑本身执行多久”,而是“用户感知延迟”或“端到端响应时间”,System.nanoTime() 就不合适了。它测不到 I/O 等待、锁竞争、GC STW、线程调度延迟等关键外部因素。
- 网络请求耗时:应该用发起前和收到响应后的
System.currentTimeMillis(),因为它反映真实挂钟流逝 - 数据库查询:驱动层或代理(如 p6spy)更可靠,它们能捕获网络+服务端+序列化全链路
- Web 接口响应:用反向代理(Nginx)或网关(Spring Cloud Gateway)的日志时间,比应用内
nanoTime更贴近用户 - 涉及
synchronized或Lock的代码块:nanoTime只测到进入临界区的时间,不包括排队等待时间
真正需要纳秒级定位的,通常是算法微优化、缓存行对齐、CAS 重试次数统计这类底层性能分析场景,而且往往得配合 async-profiler 或 perf 使用。

















