应使用 system.nanotime() 测算法耗时,因其纳秒级精度、高分辨率、不受系统时间调整影响,且调用开销极低;而 currenttimemillis() 毫秒级精度不足,无法捕捉微秒级性能差异。

测算法耗时,别用 currentTimeMillis()。它返回毫秒值,Windows 上分辨率常达 15 毫秒,Linux 也多在几毫秒级——连一次空方法调用都可能测不出差异。真正适合算法性能分析的,是 System.nanoTime():它不报“几点”,只记“过了多久”,且起点固定、单调递增、不受系统时间调整影响。
为什么 nanoTime 是算法计时的唯一合理选择
算法优化关注的是微小差异:比如排序从 O(n²) 降到 O(n log n),实际执行可能只快几十微秒;缓存命中与未命中的差距常在百纳秒量级。这些变化,currentTimeMillis() 根本无法捕捉。
- 精度单位不同:nanoTime 返回纳秒整数,currentTimeMillis 返回毫秒整数,前者理论精度高 10⁶ 倍
- 分辨率真实更高:Linux 下通常 1–15 纳秒,Windows 借助 QueryPerformanceCounter 也能稳定在 100 纳秒左右
- 绝对不受 NTP 或手动改时间干扰:避免出现负耗时或统计突跳,保障压测数据可信
- 无历法/时区开销:纯内核或硬件计数器读取,单次调用开销仅约 50 纳秒,远低于业务逻辑本身
正确测量单次算法耗时的操作要点
直接套用 nanoTime() 很容易出错。关键不在“怎么调”,而在“怎么减”和“怎么用”。
- 起止调用必须紧邻被测代码,中间不插入日志、变量赋值或任意非必要语句
- 用
try-finally保证结束时间一定采集到,防止异常中断导致漏记 - 差值结果只做减法,不转成 Date、不参与时间比对、不跨 JVM 传递
- 不要打印原始 nanoTime 值(如
System.nanoTime() + "ns"),它没有业务含义
规避常见失真陷阱
即使用了 nanoTime,以下因素仍会让结果偏离真实算法耗时:
- JIT 编译未预热:前几百次调用含类加载、解释执行、热点编译开销,建议空跑 10,000 次再开始采集
- GC 干扰:一次 Young GC 可能暂停线程数十毫秒,应开启
-XX:+PrintGCDetails观察是否发生 - 单次测量噪声大:nanoTime 自身调用成本与极短操作相当,推荐批量执行 N 次后总耗时除以 N
- 虚拟化环境漂移:Docker 启用 CPU quota 时,TSC 计数可能非线性,需结合 host 时间源交叉验证
微秒级展示与统计建议
算法耗时最终常需以微秒(μs)呈现,但转换方式直接影响精度表达:
- 要保留原始信息用于分位数统计(如 P95/P99),用
double us = durationNs / 1000.0 - 做阈值判断(如“是否超过 50μs”),可用整数截断:
long us = durationNs / 1000 - 若需四舍五入更贴近感知,用
Math.round(durationNs / 1000.0) - 切忌混用单位:nanoTime 差值不能和 currentTimeMillis 结果相加或比较,语义完全不兼容
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











