性能测试应使用 system.nanotime() 而非 currenttimemillis(),因其基于单调计数器、精度高(通常≤100纳秒)、不受系统时间调整影响,而后者精度低、可能倒流、易受外部干扰。

性能测试中该用 System.nanoTime(),而不是 System.currentTimeMillis()。原因不是“纳秒比毫秒听起来更高级”,而是两者设计目标完全不同:一个专为测“花了多久”,另一个只负责答“现在几点”。
测耗时,nanoTime 是唯一合理选择
算法对比、方法执行时间、锁开销、循环基准等场景,核心诉求是获得稳定、可比、无干扰的间隔值。nanoTime 满足全部要求:
- 返回值基于单调计数器,不受 NTP 同步、手动调时、闰秒影响,差值恒为正
- 实际分辨率通常在 100 纳秒以内(Linux/macOS 常达 1 纳秒,Windows 约 100 纳秒),能区分微秒级差异
- 即使被测代码仅执行几十纳秒,多次调用 nanoTime 也能给出有意义的差值
- JVM 规范保证其差值反映真实经过时间(只要不溢出)
currentTimeMillis 不适合性能测试
它返回的是系统墙钟时间戳,本质是“日历时间代理”,天然不适合测耗时:
- 精度低:Windows 默认约 15.6ms,Linux 通常 1–10ms,两次调用常返回相同值,导致测出 0ms
- 可能倒流:系统时间向后调整时,差值为负,逻辑崩溃(如超时判断失效)
- 受外部干扰:NTP 校准、管理员改时间、虚拟机偷时间都会污染结果
- 单位是毫秒,无法表达纳秒/微秒级差异,掩盖真实性能特征
常见误用与正确写法
错误做法会悄悄破坏测试可信度:
- 用 currentTimeMillis 测 for 循环单次迭代——大概率得到 0,或跳变值
- 把 nanoTime 值当成时间戳转成 LocalDateTime——它没有日历含义,转换毫无意义
- 混用两者计算:startTime = System.nanoTime(); deadline = System.currentTimeMillis() + 5000; ——起点不同、单位不同、语义冲突
正确模式很简单:
long start = System.nanoTime();doWork();
long elapsedNs = System.nanoTime() - start;
// 转毫秒:elapsedNs / 1_000_000.0
进阶提醒:别裸写 nanoTime 做压测
单次 nanoTime 差值易受 JIT 预热、GC、CPU 频率波动干扰。真正做算法效率对比,请用 JMH:
- JMH 自动处理预热轮次,避开解释执行阶段
- 隔离 GC 影响,提供统计置信区间和误差分析
- 底层仍依赖 nanoTime,但封装了所有工程细节
- 避免自己实现“取平均”“剔除极值”等不严谨操作
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











