准确测量java方法耗时需预热10,000次、多轮采样1,000次以上,仅包裹纯cpu逻辑,用volatile防止jit优化,剔除gc污染样本,采用截尾均值与直方图计算p99/p999,纳秒转微秒须谨慎避免精度丢失。

不能靠单次 System.nanoTime() 打点就下结论。它提供的是纳秒级差值,但真实方法耗时会被 JIT 编译、GC 暂停、线程调度、CPU 频率跳变等干扰掩盖。准确测量的关键不是“调用两次”,而是构建一套抗干扰、可复现、有统计意义的基准流程。
必须包裹真实业务逻辑,而非整个方法外壳
起始和结束的 nanoTime() 调用,必须紧贴你要评估的那部分计算逻辑,中间不掺杂日志、参数校验、锁等待或 I/O 等外部依赖:
- ❌ 错误:在
run()或doPost()开头记 start,结尾记 end —— 这测的是端到端延迟,含大量非业务开销 - ✅ 正确:只包住纯 CPU 密集操作,例如
json.parse()、sort()、加密解密或数值计算 - 示例:
long start = System.nanoTime();
int result = fastHash(input); // 关键算法
long end = System.nanoTime();
long costNs = end - start;
预热 + 多轮采样是硬性前提
未预热的首次调用会混入类加载、JIT 解释执行甚至 C2 编译(可能达数十毫秒),导致数据断层:
- 至少空跑 10,000 次目标方法,丢弃前 10%~20% 的样本
- 正式采集不少于 1,000 次,避免单点噪声主导结果
- 对每次耗时做纳秒级记录,保留原始值(不做单位转换),聚合时再统一处理
- 推荐用
volatile int sink接收计算结果,防止 JIT 将整段逻辑优化掉
生产环境需识别并剔除污染样本
GC 暂停、线程抢占、跨核 TSC 偏差会让部分样本严重失真,不能简单求平均:
- 每次 flush 缓冲区前,检查
GarbageCollectorMXBean.getCollectionCount()是否变化;若变化,整批标记为 “GC-contaminated”,不参与主指标计算 - 使用环形缓冲区(如 ThreadLocal
+ 4096 容量),避免频繁对象分配触发 Minor GC - 聚合时采用截尾均值(trimmed mean):去掉最高 5% 和最低 5% 的极值,再对剩余 90% 求平均
- P99/P999 必须单独走直方图路径,不能从均值推导
单位转换要谨慎,避免精度丢失
纳秒值本身无单位语义,转微秒或毫秒时方式不同,影响分析结论:
- 日志打点或阈值判断可用整数截断:
costNs / 1000(单位:μs) - 做平均或绘图分析,务必用浮点运算:
costNs / 1000.0(保留小数位,避免系统性低估) - 不要用
TimeUnit.NANOSECONDS.toMicros()—— 多一层方法调用,对微秒级测量构成干扰 - 注意:底层分辨率通常在 10–100 纳秒,显示 “1234.56 μs” 不代表物理精度达 0.01 μs,而是当前计时器线性映射的结果











