JVM也是玄学

前段时间,

@陈亮 发现一件怪事,
java循环长度的相同、循环体代码相同的两次for循环的执行时间相差了100倍

有些同学说这是因为GC或者cache missing。但我掐指一算,这些同学可能是错的。

因为,就算空跑这么多次循环也不止15毫秒。

考虑到15毫秒是Windows里计时器的最小间隔,跑出15毫秒只有一个可能:JVM的优化编译器发现整段循环都没有副作用,于是把整个循环全删了。

不过回答装箱拆箱的同学可能沾点边,因为这段代码中,JVM决定是否能把整段循环删掉取决于它怎么分析Integer.valueOf。

在JVM不优化的情况下,每次内层循环会调用Integer.value 8000次,其中有读取7999次缓存,创建1次new Integer。如果JVM把valueOf和new Integer都内联,然后做了逃逸分析的话,new Integer是可以完全优化掉的。

虽然new Integer调用次数很少,但是JVM需要把new Integer内联之后才能分析出整段代码都没有副作用。所以我猜测new Integer是否被内联可能是影响性能的关键因素。

不幸的是,JVM的优化行为,对用户来说很难预测。

比如实际上new Integer的调用次数相当少。由于new Integer不是热点,JVM倾向于不内联new Integer那也不奇怪。

更难预测之处在于,在嵌套函数调用的情况下,JVM先内联里层还是先内联外层也会对优化结果产生影响。本文的例子倒还好说,如果涉及forEach之类的高阶函数调用时,会碰上monomorphic call的问题,虽然外层函数调用次数很少,但必须内联外层才能消除内层的虚函数调用。

总而言之,这种代码属于JVM的阴暗角落,就好像C语言里的i+++++i一样恶心。对于应用开发者来说,如果不是特别在乎性能的场合,别碰。甚至说不定升级一下JVM,性能就自动变好了。

    原文作者:JVM
    原文地址: https://juejin.im/entry/5be549d7e51d45132b46f2fb
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞