关键要点
- 分析程序的性能非常重要:您知道用于分析的开源工具吗?
- 有两种主要类型的分析器:采样分析器和检测分析器;了解它们之间的差异将帮助您选择合适的类型
- 三种主要开源分析器各有优缺点:简单的分析器 (VisualVM),具有许多特性的可分析器 (async-profiler),内置分析器获得了大量的额外信息 (JMC)
- 所有这些分析器都是类似于结果的采样分析器,这使得它们比其他分析器干扰更快,但也需要 Java 支持运行
- 使用分析器并非没有风险,有时会导致性能下降和罕见的崩溃
探测器的目的是获取相关程序执行的信息,以便开发人员能够了解某种方法在给定的时间内执行了多长时间。
但是分析师是如何做到这一点的呢?获取配置文件的方法有两种:检测程序和采样。
检测分析器获取配置文件的一种方法是记录开发人员感兴趣的每种方法的进出。
当许多开发人员想知道他们的程序的特定部分花了多长时间时,他们已经在做这个测试了。
因此,以下方法:
void methodA() { // … // do the work}
复制
相关信息修改记录:
void methodA() { long start = System.currentTimeMillis(); // … // do the work long duration = System.currentTimeMillis() - start; System.out.println(“methodA took “ + duration + “ms”);}
复制
这种修改可以测量基本时间。然而,它在嵌套测量方法中提供的信息很少,因为理解方法之间的关系也很有趣,例如methodB()
was executed in seconds by methodA()
。因此,我们需要将每次进出记录在相关方法中。这些日志与时间戳和当前线程有关。
检测分析器的想法是自动执行此代码修改:它将是正确的logEntry()
和logExit()
该方法的呼叫被插入该方法的字节码中。这些方法是探测器运行时库的一部分。该插入通常在运行过程中完成,并在加载类时使用检测代理。然后分析器将我们的methodA()
from before 修改为:
void methodA() { logEntry(“methodA”); // … // do the work logExit(“methodA”);}
复制
Instrumenting 分析器的优点是它们可以与所有分析器相匹配 JVM 一起工作,因为它们可以纯粹使用 Java 实现。但它们的缺点是,插入的调用会导致显著的性能损失和严重的扭曲。因此,近几十年来,纯仪器分析器的流行程度已经消退。现代分析师大多是采样分析师。
采样刻画器另一种类型的分析器是从分析程序执行中获取样本的采样分析器。这些分析器定期向前移动 JVM 询问目前正在运行的程序堆栈,通常是 10 毫秒到 20 毫秒。然后分析器可以使用这些信息来接近配置文件。但这给我们带来了主要的缺点:从配置文件中可能看不到更短的操作方法。
抽样分析器的主要优点是,它们以低成本分析未修改的程序,而不显著扭曲结果。
现代采样分析器通常通过 10 到 20 毫秒循环运行以下命令工作:
每次迭代都可以使用采样分析器 (Java) 线程列表。然后选择随机线程子集进行采样。这个子集的大小通常是 5 到 8 在此之间,每次迭代中采样过多的线程会增加运行分析器的性能影响。请注意这一事实,当分析具有大量线程的应用程序时。
然后分析器向每个选定的线程发送一个信号给每个线程,导致它们停止并单独调用一个信号处理程序。该信号处理程序获取并存储其线程的堆栈跟踪。收集每次迭代结束时的所有堆栈跟踪并进行后处理。
还有其他方法可以实现采样分析器,但我向您展示了使用最广泛、精度最高的技术。
不同的开源分析器有三个著名的开源分析器:VisualVM、async-profiler 和 JDK Flight Recorder (JFR)。这些分析器正在积极开发,可用于各种应用程序。它们都是采样分析器。VisualVM 它是唯一支持仪器分析的分析器。
我们可以区分“外部”和“内置”分析器:外部分析器不直接实现 JVM 中间,但使用 API 收集特定线程的堆栈跟踪。只使用 API 对于具有相同分析器版本的不同分析器 JVM 版本和供应商(例如 OpenJDK 和 OpenJ9)。
两个最著名的外部分析器是 VisualVM 和 async-profiler;它们的主要区别元素是它们使用的 API。VisualVM 使用官方的Java 管理扩展(JMX) 获取线程的堆栈跟踪。另一方面,Async-profiler 使用非官方的 AsyncGetCallTrace API。两者各有利弊,但是 JMX 和相关 API 它通常被认为更安全,AsyncGetCallTrace 更精确。
OpenJDK 和 GraalVM 唯一的内置分析器是 Java Flight Recorder (JFR);其工作原理及 async-profiler 大致相同,同样精确但稍微稳定一点。
下一节我将介绍不同的分析师及其历史。
虚拟机此工具是 Netbeans 独立版本的分析器。从 2006 年的 Oracle JDK 6 到 JDK 8,每个 JDK 都包含 Java VisualVM 工具,这个工具在 2008 年开源。该分析器后来更名为 VisualVM,而 Oracle 不包括在内 JDK 9 中。根据最近的情况 JetBrains调查, VisualVM 它是最常用的开源分析器。
它的使用非常简单;只需要 GUI 选择运行您想要分析的程序 JVM 并触发分析:
然后,您可以直接在简单的树可视化中查看配置文件。样本分析器也可以使用以下命令从命令行开始和停止:
visualvm --start-cpu-sampler <pid>visualvm --stop-sampler <pid>
复制
VisualVM 它是一种简单易用的分析器 UI,但是,注意不要使用特定的特定用途 JVM API。
异步分析器最常用的分析器之一是 async-profiler,特别是因为它嵌入了许多其他工具,例如 IntelliJ Ultimate Profiler 和 AppIication Performance Monitors。你可以从它的项目开始的 GitHub 页面下载 async-profiler 。它在 Windows 不受支持,由特定于平台的二进制文件组成。我创建了它ap-loader 项目,它将所有 async-profiler 二进制文件包装在多平台二进制文件中,使分析器更容易嵌入和使用。
您可以使用许多嵌入其中的工具或直接将其用作本机 Java 代理来使用 async-profiler。假设您在平台上下载了特定的假设 libasyncProfiler.so,您可以添加以下选项 Java 调用二进制文件分析您 Java 应用程序:
java -agentpath:libasyncProfiler.so=start,event=cpu,file=flame.html,flamegraph …
复制
这个呼叫将告诉异步分析器生成火焰图,这是一个流行的可视化。你也可以用它来创建它 JFR 文件:
java -agentpath:libasyncProfiler.so=start,event=cpu,file=profile.jfr,jfr …
复制
这个调用程序允许您在许多查看器中查看配置文件。
一些好奇的异步分析器的历史:
2002 年 11 月,Sun(后来被 Oracle 收购)根据JVM(TM) 工具接口规范向 JDK 添加了 AsyncGetStackTrace API 。新的 API 有可能从外部分析器中获得准确的堆栈跟踪。Sun 引入了此 API 以将完整的 Java 添加到分析器中的分析器 Sun Development Studio。两个月后,出于不为人知的原因,他们删除了它 API。但是该 API 作为 AsyncGetCallTrace 保留在 JDK 直到今天,它仍然存在,但没有导出,所以使用起来更困难。
几年后,人们偶然发现了这一点 API 这是实现分析器的好方法。第一次公开提及 AsyncGetCallTrace 作为 Java 分析器的基础是 Jeremy Manson 在他 2007 在年度博客文章中,标题是Profiling with JVMTI/JVMPI, SIGPROF and AsyncGetCallTrace。从那时起,许多开源和闭源分析器就开始使用它。值得注意的例子是YourKit、JProfiler和honest-profiler。async-profiler 的开发始于 2016 年;目前正在使用 AsyncGetCallTrace 主要开源分析器。
async-profiler 问题在于它基于非官方内部 API。此 API 未在官方 OpenJDK 经过充分测试,测试套件随时可能崩溃。尽管 API 广泛使用导致标准化,但仍存在风险。为了降低这些风险,我目前正在研究它们 JDK 增强提案,官方提案 AsyncGetCallTrace 版本添加到 OpenJDK;
async-profiler 它的优点是它的许多特点(如堆采样)、可嵌入性,对其他 JVM(如 OpenJ9的支持及其小代码库使其易于适应。
JDK 飞行记录器 (JFR)JRockit 最初为内部使用开发分析器,但也越来越受到应用程序开发人员的欢迎。后来在 Oracle 这些特征在收购开发公司后被集成 Oracle JDK 中。Oracle 最终使用 JDK11 从那时起,开源了这个工具,OpenJDK 的 JVM 没有其他时间间隔分析工具 JVM(如 OpenJ9)的支持。
它的工作方式和 async-profiler 相当的主要区别在于它直接使用内部 JVM API。通过向对方添加以下选项 Java 探测器在调用二进制文件时易于使用:
$ java \ -XX:+UnlockDiagnosticVMOptions \ -XX:+DebugNonSafepoints \ # improves precision -XX:+FlightRecorder \ -XX:StartFlightRecording=filename=file.jfr \ arguments
复制
或者使用 JDK 启动和停止命令工具jcmd
:
$ jcmd PID JFR.start$ jcmd PID JFR.dump filename=file.jfr$ jcmd PID JFR.stop
复制
JFR 捕获许多分析事件,从采样堆栈跟踪到垃圾收集和类加载统计信息。查看JFR Events获取所有活动列表的网站。甚至可以添加自定义事件。
JFR 相对于 async-profiler 其主要优势在于它包含在所有平台上 OpenJDK 中,甚至在 Windows 上面也是如此。JFR 事件和信息也被认为稍微稳定一点,记录得更多。JFR 有一个 GUI,称为 JDK Mission Control,它允许你分析 JVM 并检查生成 JFR 配置文件。
正确和稳定使用我介绍的分析器时,请记住以下几点:它们只是软件和相当大的项目 OpenJDK(或 OpenJ9,就此而言)交织在一起,因此会遇到用它们来分析应用的典型问题:
- 测试可以更丰富,尤其是底层API,可以更好的测试;目前只有一个测试。(我在做这个工作)
- 测试可能更好:现有测试甚至没有完全测试 API 它是否适用于小样本。它只检查了顶部框架,但没有发现返回轨迹太短。我发现了这个问题,并修复了测试用例。
- 缺乏自动回归测试:缺乏测试也意味着在没有人注意到的情况下,封闭项目中看似无关部分的变化可能对分析产生不利影响。
用于 Java 基于采样的现代分析器使用开源工具调查性能问题成为可能。您可以选择:
- 一个稍微不准确但容易使用的工具,简单 UI (VisualVM)
- 包含 GC 内置工具等信息 (JFR)
- 一个可以显示多种选项的工具 C/C++ 代码的信息(async-profiler)