简单的云服务器搭建代理ip
在本文中,我将介绍性能分析的基本概念和不同类型的开源 Java 分析器,让你可以根据自己的需要选择最适合的分析器,并了解这些工具大致的工作原理。
在 2023 年伦敦 QCon 演讲“你的 Java 应用程序很慢吗?试试这些开源分析器”中,我深入探讨过这个话题,也介绍了不同的性能查看器。本文是基于那次演讲整理而成。
分析器的目的是获取有关程序执行的信息,让开发人员可以看到一个方法在给定的时间段内执行了多长时间。
这种修改可以用于基本的时间测量。尽管如此,在嵌套测量方法时,它提供的信息很少,因为了解方法之间的关系也很有趣,例如methodB()由methodA()在几秒钟内执行。因此,我们需要记录每次进入和退出相关方法的日志。这些日志会关联到时间戳和当前线程。
插桩分析器的思想是将这种代码修改的过程自动化:它将logEntry()和logExit()方法的调用插入到方法的字节码中。这些方法是分析器运行时库的一部分。通常,这种插入是在运行时完成的,即在类加载时通过插桩代理完成。然后,分析器将methodA()修改为:
插桩分析器的优点是它们对所有 JVM 都有效,因为它们可以用纯 Java 实现。但它们有一个缺点,即插入的方法调用会导致显著的性能损失并严重影响结果。因此,在最近几十年里,纯插桩分析器的流行度已然消退。如今,现代分析器大多都是抽样分析器。
另一种分析器是抽样分析器,它们会在被分析程序执行时进行抽样。这类分析器会定期向 JVM 请求当前运行程序的堆栈,通常是每 10 毫秒到 20 毫秒一次。然后,分析器会使用这些信息来估算性能。这种方法的主要缺点是:运行时间比较短的方法可能不会在性能分析概要中出现。
抽样分析器每次迭代都会获取当前的(Java)线程列表。然后,它会随机选择一个线程子集进行抽样。通常,这个子集的大小在 5 到 8 之间,因为每次迭代对太多线程进行抽样会增加运行分析器的性能影响。在分析具有大量线程的应用程序时,请注意这一点。
然后,分析器向每个选定的线程发送一个信号,这将导致它们停下来调用信号处理程序。此信号处理程序会获取并存储其线程的堆栈跟踪。在每次迭代结束时,分析器会收集所有堆栈跟踪信息并进行后处理。
目前,最著名的开源分析器有 3 个:VisualVM、async-profiler 和 JDK Flight Recorder(JFR)。这些分析器都处于积极开发过程中,可用于各种应用程序。它们都是抽样分析器。VisualVM 是唯一支持插桩分析的分析器。
我们可以区分下“外部”和“内置”分析器:外部分析器不是直接实现到 JVM 中,而是使用 API 来收集特定线程的堆栈跟踪信息简单的云服务器搭建代理ip。对于只使用 API 的分析器,同一个版本可以用于不同的 JVM 版本和供应商(如 OpenJDK 和 OpenJ9)。
它的用法很简单;只需要在 GUI 中为你想要分析的程序选择运行它的 JVM 并启动性能分析:
然后,你可以在一个简单的树形可视化中直接查看性能分析概要信息。也可以从命令行启动和停止抽样分析器:
VisualVM 提供了易于使用的简单 UI,但需要注意,它使用了不太精确的 JVM API。
你可以通过许多嵌入了 async-profiler 的工具使用它,或直接将其作为本机 Java 代理来使用。假设你下载了特定于平台的 libasyncProfiler.so,则只需在调用 Java 二进制文件时添加以下选项,即可分析 Java 应用程序的性能:
2002 年 11 月,Sun(后来被 Oracle 收购)根据 JVM(TM)工具接口规范将 AsyncGetStackTrace API 添加到 JDK 中。新 API 使得从外部分析器获得精确的堆栈跟踪信息成为可能。Sun 引入这个 API 是为了给他们的 Sun Development Studio 添加一个完整的 Java 分析器。然而,两个月后,他们删除了该 API,原因未公开。但是,这个 API 仍然以 AsyncGetCallTrace 的形式保留在 JDK 中,直到今天一直存在,只是没有导出,所以比较难用。
Async-profiler 的问题在于,它是基于一个非官方的内部 API。这个 API 没有经过官方 OpenJDK 测试套件的充分测试,随时都可能失效。尽管该 API 的广泛应用使得它已近乎标准化,但这仍然是一个风险。为了减轻这些风险,我目前正在编制一份 JDK 增强提案,在 OpenJDK 中增加一个官方的 AsyncGetCallTrace 版本;见 JEP 候选 435。
JRockit 最初开发运行时分析器是为了内部使用,但它也越来越受应用程序开发人员的欢迎。后来,在 Oracle 收购了其开发公司之后,这些特性被集成到了 Oracle JDK 中。最终,Oracle 将该工具与 JDK11 一起开源,从那时起,它就成了 OpenJDK JVM 的内置分析工具,不再支持 OpenJ9 等其他 JVM 了。
它的工作原理与 async-profiler 类似,主要区别是它直接使用内部的 JVM API。该分析器的使用很简单,可以通过在 Java 二进制文件的调用中添加以下选项:
JFR 捕获许多性能分析事件,从堆栈跟踪信息抽样到垃圾收集和类加载统计信息。JFR 事件网站上提供了所有事件的列表。我们甚至还可以添加自定义事件。
与 async-profiler 相比,JFR 的主要优势是它存在于所有平台的 OpenJDK 中,甚至在 Windows 上。此外,JFR 更稳定一些,记录的事件和信息也更多。JFR 有一个名为 JDK 任务控制的 GUI,它让你可以分析 JVM 性能并查看生成的 JFR 性能分析概要。
在使用我所介绍的分析器时,务请记住以下内容:它们本身也是软件,与大型项目 OpenJDK(或 OpenJ9)交织在一起,因此,它们也会遇到与它们所分析应用程序相同的典型问题:
测试可以做得更好:现有的测试甚至没有充分测试 API 是否适用于小样本。它只检查了最上面的帧,但忽略了返回的跟踪信息太短这个问题。我发现了这个问题并修复了测试用例。
缺乏自动化回归测试:缺乏测试还意味着,对当前项目中看似不相关部分的更改可能会对分析产生不利的影响,而又没有人注意到。
因此,对于分析器生成的性能分析概要,你要持保留态度。以下博文和演讲谈及了分析器的准确性问题:
此外,在极少数情况下,对应用程序进行性能分析还可能导致 JVM 崩溃。像 Jaroslav Bachorik 和我这样的 OpenJDK 开发人员正设法尽可能地修复底层分析 API 中存在的所有稳定性问题。在实践中,使用上面提到的任何一种分析器都是安全的,很少会引发崩溃。如果遇到问题,请联系分析程序开发人员或在相应的存储库中开一个 GitHub 问题。