是的,你没有看错,Java代码段中方法的长度是会影响代码性能的。
最近对代码进行性能测试时发现一个很诡异的问题,在低并发的情况下,多线程的性能增长不是线性的,对问题定位后发现奇怪的现象:
当一个方法字节码索引号超过8000时,每个线程执行该代码段时会带来额外的几十毫秒开销,最终测试结果如下:
当我把该方法中任意部分抽出一个方法后,测试结果如下:
而我做的仅仅是把任意一行代码抽出了一个方法,并在原位置调用它:
这两个版本的区别仅在于字节码少了3行:
将方法中任意代码段抽出,都能解决这个问题。且这个问题仅在多线程下复现。(起码两个线程)
HotSpot VM默认不会JIT编译字节码大小超过8000字节的方法。
HotSpot有两个参数控制这一行为:
-XX:+DontCompileHugeMethods
-XX:HugeMethodLimit=8000
DontCompileHugeMethods
与HugeMethodLimit
的值在globals.hpp
中定义:
define_pd_global(intx, ReservedCodeCacheSize, 48M);
product_pd(uintx, InitialCodeCacheSize, “Initial code cache size (in bytes)”)
product_pd(uintx, ReservedCodeCacheSize, “Reserved code cache size (in bytes) - maximum code cache size”)
product(uintx, CodeCacheMinimumFreeSpace, 500K, “When less than X space left, we stop compiling.”)
如果方法字节码超过8000字节,默认情况下JVM是不会对这方法进行JIT编译的,也就享受不到JIT编译带来的性能优化了。
由于HugeMethodLimit
的值在产品版HotSpot里无法调整,
临时的解决办法是使用-XX:-DontCompileHugeMethods
参数来允许大方法被JIT编译,不过使用这个参数后要注意配置CodeCache区的大小,以免满了之后HotSpot停止所有后续的编译任务。
一劳永逸的办法当然是把大段的代码抽成一段段小逻辑啦。