我广泛使用 jProfiler,它是一个很棒的工具,但我想知道 jProfiler 如何处理 JIT 编译的效果。
即时编译实际上是一项相当古老的技术,已被 HotSpot 自适应优化器取代。JIT 盲目地将 Java 字节码中的每个方法编译为本机代码,并且可能(或可能不会)能够很好地优化它。
但是,自适应优化器会查看代码当前的运行方式,并仅优化最常执行的代码。因为优化器知道代码是如何被使用的,所以优化器可以并且做更好的方法内联、分支预测、锁粗化、循环展开等等。
我可以观察例如方法内联吗?如果一个方法是内联的,它在快照中根本不可见,还是 jProfiler 仍然能够计算它的执行时间?
JProfiler 不会报告哪些方法被内联了;但是,即使编译器将它们内联在另一个方法中,它也会愉快地报告它们的调用。
如何?好吧,在编译过程中,编译器在本机代码中划分方法边界。这对于 JVM 在发生异常或错误时能够重建堆栈跟踪至关重要。当对 JVM 进行采样时,JVM 会以正在执行的线程的堆栈跟踪进行响应,因此即使是内联的,也可以准确地报告当前方法。
同样,没有副作用并且可以完全优化掉的方法也不会显示在 jProfiler 中。它是否正确?
你几乎是正确的。一个没有副作用或计算返回值的方法或调用另一个可能有副作用的方法几乎完全被优化掉了,但是在 JVM 中有一个化石残余记录了该方法将被调用。边际开销非常小,但具有以下有趣的特征:
- 采样时,此方法在采样时极不可能在堆栈中,因此 jProfiler(或其他采样分析器)很可能不会检测到。
- 检测时,分析器会跟踪所有方法调用并检测方法调用。如上所述,总成本将非常小。
本文解释了采样与检测的区别。