方法调用记录
记录方法调用是分析器最困难的任务之一,因为它在相互冲突的约束下运行:结果应该准确、完整,并产生如此小的开销,以至于您从测量数据中得出的结论不会变得不正确。不幸的是,没有一种测量类型可以满足所有类型应用程序的所有这些要求。这就是为什么JProfiler要求您决定使用哪种方法。
采样与插桩
方法调用的测量可以通过两种根本不同的技术完成,称为“采样”和“插桩”,每种技术都有其优点和缺点:通过采样,线程的当前调用栈会定期检查。通过插桩,选定类的字节码被修改以跟踪方法的进入和退出。插桩测量所有调用,并且可以为所有方法生成调用计数。
在处理采样数据时,完整的采样周期(通常为5毫秒)归因于采样的调用栈。随着大量样本的出现,统计上正确的图像会浮现。采样的优点是它的开销非常低,因为它很少发生。不需要修改字节码,并且采样周期比方法调用的典型持续时间要长得多。缺点是您无法确定任何方法调用计数。此外,仅调用几次的短时间运行方法可能根本不会出现。如果您正在寻找性能瓶颈,这并不重要,但如果您试图了解代码的详细运行时特性,这可能会带来不便。
另一方面,如果对许多短时间运行的方法进行插桩,可能会引入大量开销。这种插桩会扭曲性能热点的相对重要性,因为时间测量的固有开销,但也因为许多本来会被热点编译器内联的方法现在必须保持为单独的方法调用。对于需要较长时间的方法调用,开销是微不足道的。如果您可以找到一组主要执行高级操作的类,插桩将增加非常低的开销,并且可能比采样更可取。JProfiler的开销热点检测也可以在一些运行后改善情况。此外,调用计数通常是重要的信息,使得更容易看出发生了什么。
完整采样与异步采样
JProfiler为采样提供了两种不同的技术解决方案:“完整采样”是通过一个单独的线程完成的,该线程定期暂停JVM中的所有线程并检查它们的栈跟踪。然而,JVM仅在某些“安全点”暂停线程,从而引入偏差。如果您有高度多线程的CPU绑定代码,被分析的热点分布可能会被扭曲。另一方面,如果代码也执行了大量I/O,这种偏差通常不会成为问题。
为了帮助获得高度CPU绑定代码的准确数字,JProfiler还提供异步采样。通过异步采样,分析信号处理程序在运行的线程本身上被调用。分析代理然后检查本机栈并提取Java栈帧。主要的好处是这种采样方法没有安全点偏差,并且对于高度多线程的CPU绑定应用程序,开销较低。然而,对于CPU视图来说,只能观察到“Running”线程状态,而“Waiting”、“Blocking”或“Net I/O”线程状态无法以这种方式测量。探针数据始终通过字节码插桩收集,因此您仍然可以获得JDBC和类似数据的所有线程状态。
异步采样存在截断的跟踪,其中仅可用调用栈的末尾。这就是为什么调用树对于异步采样通常不如热点视图有用的原因。异步采样仅在Linux和macOS上受支持。
从Java 17开始,JProfiler可以避免在Hotspot JVM上使用全局安全点进行采样,并以接近零的开销进行完整采样。与异步采样相比,它仍然为单个线程引入某种安全点偏差,但不再为JVM中的所有线程引入全局安全点的开销。考虑到异步采样的缺点,建议在Java 17+上使用完整采样。
选择方法调用记录类型
选择用于分析的方法调用记录类型是一个重要的决定,并且没有适用于所有情况的正确选择,因此您需要做出明智的决定。当您创建新会话时,会话启动对话框会询问您要使用哪种方法调用记录类型。在任何以后的时间点,您都可以在会话设置对话框中更改方法调用记录类型。
作为一个简单的指南,请考虑以下问题,以测试您的应用程序是否属于光谱两端的两个明确类别之一:
被分析的应用程序是否I/O绑定?
许多Web应用程序大部分时间都在等待REST服务和JDBC数据库调用。在这种情况下,插桩将在您仔细选择调用树过滤器以仅包含您自己的代码的条件下是最佳选择。被分析的应用程序是否高度多线程且CPU绑定?
例如,这可能是编译器、图像处理应用程序或正在运行负载测试的Web服务器的情况。如果您在Linux或macOS上进行分析,您应该选择异步采样以在这种情况下获得最准确的CPU时间。
否则,“完整采样”通常是最合适的选项,并建议作为新会话的默认选项。
本机采样
因为异步采样可以访问本机栈,所以它也可以执行本机采样。默认情况下,本机采样未启用,因为它会在调用树中引入大量节点,并将热点计算的重点转移到本机代码。如果您确实在本机代码中遇到性能问题,您可以选择异步采样并在会话设置中启用本机采样。
JProfiler解析属于每个本机栈帧的库路径。在调用树中的本机方法节点上,JProfiler在开头以方括号显示本机库的文件名。
关于聚合级别,本机库的作用类似于类,因此在“类”聚合级别中,同一本机库内的所有后续调用将聚合到一个节点中。“包”聚合级别将所有后续本机方法调用聚合到一个节点中,而不考虑本机库。
要消除选定的本机库,您可以从该本机库中移除一个节点并选择移除整个类。