System.nanoTime() 是 Java 中唯一适合高性能计时与性能分析的原生工具,它不依赖系统墙钟、天生单调递增、分辨率通常达 10–15 纳秒,但必须严格按规范使用——只用于同一线程内测时间差,返回值无业务意义,不可存储或跨线程传递,微秒转换推荐浮点除法,基准测试需预热、批量采样与抗干扰处理。

System.nanoTime() 是 Java 中唯一适合高性能计时与性能分析的原生工具,它不依赖系统墙钟、天生单调递增、分辨率通常达 10–15 纳秒,但必须严格按规范使用——它不是“更准的 currentTimeMillis”,而是一把专用的高精度“游标卡尺”,只用于测时间差。
只做减法,不存值、不跨线程、不跨 JVM
它的返回值本身无业务意义:不能打印、不能序列化、不能存数据库、不能传给其他线程或进程。唯一合法操作是同一线程内两次调用后立即相减。例如:
- ✅ 正确:long start = System.nanoTime(); doWork(); long cost = System.nanoTime() - start;
- ❌ 错误:long t1 = System.nanoTime(); Thread.sleep(10); long t2 = System.nanoTime(); // 中间挂起导致误差放大
- ❌ 危险:将 nanoTime 值作为日志时间戳写入文件——它无法对齐真实时刻,且不同 JVM 启动值不可比
微秒级耗时换算要选对除法类型
纳秒差转微秒不是简单除以 1000 就完事。Java 整数除法会截断余数,造成系统性低估:
- ⚠️ 避免:micros = nanos / 1000 → 1999ns 变成 1μs(丢失 999ns)
- ✅ 推荐浮点运算:micros = nanos / 1000.0 → 得到 1.999μs,保留原始粒度
- ? 分析用途(如 P99 统计)务必保留纳秒原始值,统一在聚合阶段转换,避免多次除法引入累积误差
基准测量必须预热 + 多轮 + 批量采样
单次调用 nanoTime 测单行代码毫无意义——JIT 编译、CPU 频率切换、TLB 缺失等干扰远大于真实耗时。可靠做法是:
- ? 先预热:执行目标方法 10,000 次以上,确保 JIT 编译完成、类已初始化、缓存已预热
- ⏱ 批量测量:每轮执行 1,000–10,000 次,仅记录总耗时,再除以次数得均值
- ? 抗干扰:采集至少 100 轮,取中位数而非平均值,有效过滤 GC 暂停、中断抖动等毛刺
警惕常见干扰源,别让计时器成为噪声源
即使用了 nanoTime,这些因素仍会让结果失真:
- ? 不要在被测代码中创建对象、拼接字符串、触发日志——它们可能引发 GC 或分配开销
- ? 避免空循环或无副作用计算——JIT 可能直接优化掉整段逻辑;用 volatile 变量捕获结果防止消除
- ? 不要高频调用 nanoTime 自身去测极短操作(如 i++)——其调用开销约 10–100ns,反而掩盖真相
- ✅ 替代方案:用 JMH 这类专业框架,它自动处理预热、分叉、GC 监控和统计校准


















