在对我的程序进行简单重构以减少垃圾收集时,我遇到了一些奇怪的效果。结果是不相关的代码总是慢了大约20%。
场景非常简单:我有一个方法层次结构,方法A调用方法B,B调用C1和C2。 A在循环中调用B. Ab,B C1和C2用于构建大型树结构。
我需要在C1和C2之间传递数据,所以我在B中创建了一个缓冲区对象,我首先传入C1以初始化它,然后进入C2以使用它。由于B被称为大约5,000,000次,这导致相当多的GC,显然大约是170MB的GC。
作为优化,我决定在A中创建缓冲区对象,然后将其传递给B以在C1和C2中使用。
结果:GC的数量下降到接近0。 但是,我有几个与A并行的方法(从不调用A,B,C1或C2),它们访问由A,B,C1,C2创建的相同数据。使用优化时,所有这些访问方法的性能都会降低约20%。
我有两个怀疑,但我不确定如何确认它们:
我正在使用Oracle JVM 64bit -Xmx28G -XX:+ UseConcMarkSweepGC,其中使用了大约5GB。 我运行了两个不同的变种几十次,结果非常清楚。运行时间差异约为20%,超过30次运行的标准偏差约为1%。两个版本的程序行为都是正确的。
如何解释这种行为? JVM内存重新排序可能是解释,还是已知存在陷阱,方法中的参数数量增加?我该如何找到?
修改
回答一些评论:
我开始使用jitwatch,看起来非常好,但显然非常低级别。
定时不应该干扰,定时/输出方法每隔几秒钟只调用一次。
缓冲区只是long[3]
(其他测试从2到15左右不等)。在一个场景中,我确实在A中保留了对缓冲区的引用,因此GC按预期为0。但是,即使我在每次调用时都在A中重新创建了缓冲区,GC仍然是0 ....