1.Java 监控工具

Java 不仅仅是一种编程语言,而是一个非常丰富的生态系统,它有很多工具。JDK 包含的程序,允许我们编译自己的程序,以及监视其状态和 Java 虚拟机在程序执行的完整生命周期内的状态。

JDK 提供的的 bin 文件夹包含可用于分析和监视的以下程序:

  • Java VisualVM (jvisualvm.exe)
  • JConsole (jconsole.exe)
  • Java Mission Control (jmc.exe)
  • Diagnostic Command Tool (jcmd.exe)

我们建议浏览此文件夹的内容,了解我们掌握的工具。

在本教程中,我们将重点介绍 Java 飞行记录器。上述工具中不存在此,因为它不是独立程序。它的使用与上述两个工具密切相关 - Java 任务控制和诊断命令工具。

2.基本概念

Java 飞行记录器 (JFR) 是一种监视工具,用于在 Java 应用程序执行期间收集有关 Java 虚拟机 (JVM) 中事件的信息。JFR 是 JDK 分布的一部分,并集成到 JVM 中。

JFR 旨在尽可能少地影响正在运行的应用程序的性能。

为了使用 JFR,我们应该激活它。我们可以通过两种方式实现此目的:

启动Java应用程序时
在 Java 应用程序已运行时传递 jcmd 工具的诊断命令
JFR 没有独立工具。我们使用 Java 任务控制 (JMC),它包含一个插件,允许我们可视化 JFR 收集的数据。

这三个组件(JFR、jcmd 和 JMC)构成了一套完整的套件,用于收集正在运行的 Java 程序的低级运行时信息。在优化程序或诊断程序时,我们可能会发现此信息非常有用。

如果我们的计算机上安装了各种版本的 Java,请务必确保 Java 编译器 (javac)、Java 启动器 (java) 和上述工具(JFR、jcmd 和 JMC)来自同一 Java 版本。否则,由于不同版本的 JFR 数据格式可能不兼容,因此存在无法看到任何有用数据的风险。

JFR 有两个主要概念:事件和数据流。让我们简要地讨论一下。

  1. Events

JFR 收集运行 Java 应用程序时在 JVM 中发生的事件。这些事件与 JVM 本身的状态或程序的状态相关。事件具有名称、时间戳和其他信息(如线程信息、执行堆栈和堆的状态)。

JFR 收集三种类型的事件:

  • 即时事件一旦发生,将立即记录
  • 如果持续时间事件成功指定阈值,则记录持续时间事件
  • 示例事件用于对系统活动进行采样
  1. Dataflow

JFR 收集的事件包含大量数据。因此,根据设计,JFR 的速度足够快,不会妨碍程序。

JFR 将有关事件的数据保存在单个输出文件 flight.jfr 中。

正如我们所知,磁盘 I/O 操作相当昂贵。因此,JFR 使用各种缓冲区来存储收集的数据,然后再将数据块刷新到磁盘。事情可能会变得稍微复杂一点,因为在同一时刻,一个程序可能有多个具有不同选项的注册进程。

因此,我们可能会在输出文件中找到比请求更多的数据,或者它可能不是按时间顺序排列的。如果我们使用 JMC,我们甚至可能没有注意到这一事实,因为它按时间顺序显示事件。

在某些极少数情况下,JFR 可能无法刷新数据(例如,当事件过多或断电时)。如果发生这种情况,JFR 会尝试通知我们输出文件可能缺少一段数据。

3.如何使用JFR

JFR 是一个实验功能,因此其使用可能会发生变化。事实上,在早期的发行中,我们必须激活商业功能,以便在生产中使用。但是,从 JDK 11 开始,我们可能会使用它而不激活任何内容。我们始终可以查阅官方的 Java 发行说明,以检查如何使用此工具。

对于 JDK 8,为了能够激活 JFR,我们应该使用选项 " +UnlockCommercialFeatures "和 " +FlightRecorder "启动 JVM。

如上所述,有两种方法可以激活 JFR。当我们在启动应用程序时同时激活它时,我们从命令行执行。当应用程序已在运行时,我们使用诊断命令工具。

3.1 命令行

首先,我们使用标准java编译器javac将程序的*.java文件编译成一个*.class。

编译成功后,我们可以使用以下选项启动程序:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder 
  -XX:StartFlightRecording=duration=200s,filename=flight.jfr path-to-class-file

其中到类文件的路径是应用程序的入口点 _.class 文件。

此命令启动应用程序并激活录制,录制将立即启动,持续不超过 200 秒。收集的数据保存在输出文件 flight.jfr 中。我们将在下一节中更详细地介绍其他选项。

3.2 诊断命令行

使用jcmd工具

jcmd 1234 JFR.start duration=100s filename=flight.jfr

在 JDK 11 之前,为了能够以这种方式激活 JFR,我们应该使用未锁定的商业功能启动应用程序:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -cp ./out/ com.baeldung.Main

应用程序运行后,我们使用其进程 ID 来执行各种命令,这些命令采用以下格式:

jcmd <pid|MainClass> <command> [parameters]

它的命令如下:

  • JFR.start – starts a new JFR recording
  • JFR.check – checks running JFR recording(s)
  • JFR.stop – stops a specific JFR recording
  • JFR.dump – copies contents of a JFR recording to file

每个命令都有一系列参数。例如,JFR.start 命令具有以下参数:

  • name – name of the recording; it serves to be able to reference this recording later with other commands
  • delay – dimensional parameter for a time delay of recording start, the default value is 0s
  • duration – dimensional parameter for a time interval of the duration of the recording; the default value is 0s, which means unlimited
  • filename – name of a file that contains the collected data
  • maxage – dimensional parameter for the maximum age of collected data; the default value is 0s, which means unlimited
  • maxsize – maximum size of buffers for collected data in bytes; the default value is 0, which means no max size

在本节的开头,我们已经看到了这些参数的使用示例。有关参数的完整列表,我们可能始终参阅 Java 飞行记录的官方文档。

尽管 JFR 设计为在 JVM 和应用程序的性能上尽可能少地占用空间,但最好通过设置至少一个参数来限制收集数据的最大量:持续时间、最大长度或最大大小。

5.例子

我们的程序把对象插入list直到出现OutOfMemeryError异常。

public static void main(String[] args) {
    List<Object> items = new ArrayList<>(1);
    try {
        while (true){
            items.add(new Object());
        }
    } catch (OutOfMemoryError e){
        System.out.println(e.getMessage());
    }
    assert items.size() > 0;
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        System.out.println(e.getMessage());
    }
}

不用运行电脑,我们能发现一个缺点:这个while循环一直运行将导致CPU 飙高和内存占用。我们使用JFR来分析一下。

5.2 开始注册

用以下命令进行编译:

javac -d out -sourcepath src/main src/main/com/baeldung/flightrecorder/FlightRecorder.java

*out/com/baeldung/flightrecorder* 目录下会发现一个文件 *FlightRecorder.class*

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder 
  -XX:StartFlightRecording=duration=200s,filename=flight.jfr 
  -cp ./out/ com.baeldung.flightrecorder.FlightRecorder

5.3 可视化

img

在视图的左侧,我们看到"常规"、内存、代码和线程等部分。每个部分包含包含详细信息的各种选项卡。例如,"代码"部分的选项卡"热方法"包含方法调用的统计信息:

img

在此选项卡中,我们可以发现示例程序的另一个缺点:方法 java.util.Array.grow(int)已调用 17 次,以便在每次没有足够的空间来添加对象时放大数组容量。

在更现实的程序中,我们可能会看到许多其他有用的信息:

有关已创建对象的统计信息,当它们被垃圾回收器创建和销毁时
关于线程的年表的详细报告,当他们被锁定或活动
应用程序正在执行的 I/O 操作。

参考代码

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐