Java两类对象反序列化性能测试结果波动原因咨询
性能测试结果波动的原因分析与优化建议
这是Java性能测试里很常见的问题,你的结果波动主要是JVM运行时特性和系统环境的不确定性共同导致的,咱们一步步拆解原因,再给你优化建议:
为什么每次测试结果不一样?
1. JIT即时编译的动态优化
Java代码刚启动时是解释执行的,JVM会在运行过程中把频繁执行的「热点代码」编译成机器码(也就是JIT编译),这个优化过程不是一次性完成的,而是逐步迭代的。你第一次测试的NIF耗时6762纳秒,后面几轮降到3400左右,就是JIT生效的表现;但后续的波动可能是因为JVM对代码做了进一步优化、或者某些编译后的代码因环境变化失效,导致执行速度不稳定。
2. 系统资源的竞争
你的测试环境不是独占的,操作系统的其他进程(比如后台服务、系统垃圾回收、磁盘IO任务)会随时抢占CPU、内存资源。比如某一轮测试刚好赶上系统GC或者磁盘备份,就会导致耗时突然上升——你第五轮NIF耗时跳到4704纳秒,很大概率是系统资源被抢占了。
3. 垃圾回收(GC)的随机触发
每次解析JSON都会生成大量临时对象(哪怕你用了HashMap复用),这些对象会被GC回收,但GC的触发时机是不确定的。如果某次测试中GC刚好在循环执行的中间发生,就会拉长整体耗时,直接导致结果波动。
4. 测试代码的小瑕疵
- 你的循环条件是
i <= cycle,实际会执行100001次,不是预期的100000次,虽然这个对波动影响不大,但细节可以修正。 - 每次循环都重新创建
ObjectMapper和Gson实例——这两个实例本身是线程安全且可以复用的,重复创建不仅增加额外开销,还会干扰JVM的优化逻辑。
优化测试的建议
- 先预热代码:在正式计时前,先跑几轮无计时的循环(比如10000次),让JVM完成JIT编译,这样正式测试的结果会更稳定。
- 复用核心实例:把
ObjectMapper和Gson移到测试方法外面,作为全局变量复用,避免重复创建的开销。 - 使用专业测试工具:推荐用JMH(Java Microbenchmark Harness),它会自动处理预热、多次采样、统计分析,能给出更可靠的性能数据。
- 控制测试环境:测试时关闭不必要的后台进程,尽量让系统资源集中在测试任务上;也可以用JVM参数(比如
-XX:+PrintGC)监控GC情况,排查波动来源。 - 取统计值而非单次结果:多跑几轮测试,取平均值、中位数或者百分位数,这样更能反映真实的性能差异,而不是被单次波动误导。
你的测试代码(格式化后)
@Test public void checkClasses() throws IOException, InterruptedException { String pkt = "BIG JSON OBJECT HERE"; for (int x = 0; x <= 5; x++) { TimeUnit.SECONDS.sleep(5); /** * Check NO FIF */ ObjectMapper om = new ObjectMapper(); HashMap hm = new HashMap<>(); int cycle = 100000; long startTime = System.nanoTime(); for (int i = 0; i <= cycle; i++) { DeviceData.fromString(pkt, om, hm); } long containsDuration = (System.nanoTime() - startTime); //divide by 1000000 to get milliseconds. long object = containsDuration/cycle; System.out.println("--- Duration NIF: " + containsDuration +",per pkt in nano:" + object ); TimeUnit.SECONDS.sleep(5); /** * Check FIF */ Gson gson = new Gson(); long fifStartTime = System.nanoTime(); for (int i = 0; i <= cycle; i++) { DeviceDataMin.fromString(pkt, gson); } long regexDuration = (System.nanoTime() - fifStartTime); //divide by 1000000 to get milliseconds. long fif = regexDuration/cycle; System.out.println("--- Duration FIF: " + regexDuration +",per pkt in nano:" + fif ); System.out.println("DIFF : " + (100 - (double) ((double)fif * 100/object))); } }
测试结果(格式化后)
--- Duration NIF: 676214146,per pkt in nano:6762 --- Duration FIF: 563609001,per pkt in nano:5636 DIFF : 16.651878142561372 --- Duration NIF: 415484807,per pkt in nano:4154 --- Duration FIF: 392932554,per pkt in nano:3929 DIFF : 5.416466056812709 --- Duration NIF: 377752426,per pkt in nano:3777 --- Duration FIF: 389876068,per pkt in nano:3898 DIFF : -3.2036007413290974 --- Duration NIF: 349583684,per pkt in nano:3495 --- Duration FIF: 392007947,per pkt in nano:3920 DIFF : -12.160228898426325 --- Duration NIF: 470443379,per pkt in nano:4704 --- Duration FIF: 368536421,per pkt in nano:3685 DIFF : 21.6624149659864 --- Duration NIF: 346816168,per pkt in nano:3468 --- Duration FIF: 354205262,per pkt in nano:3542 DIFF : -2.1337946943483246
内容的提问来源于stack exchange,提问作者TheUnreal
相关产品推荐
相关产品推荐

