除了JFR事件浏览器之外,JProfiler还使用了一些完整分析会话中的视图,并用JFR数据填充它们。这成为可能是因为JFR会收集内存分配和方法执行的数据。主要的限制在于记录速率较低,因此收集足够的数据以发现有问题的热点(hot spot)可能需要较长时间。
遥测 (telemetry)
除了“已记录对象遥测 (telemetry)”外,完整分析会话中的所有遥测 (telemetry)在JFR快照中也可用,但显示的数据有一些限制。内存遥测 (telemetry)不会显示GC专有的内存池,线程遥测 (telemetry)不会按线程状态显示线程数量,已记录吞吐量遥测 (telemetry)显示的是大小而不是对象数量,并且不会显示被释放的对象。
下表展示了各类遥测 (telemetry)所使用的事件类型,以及它们是否在“default”和“profile”模板中启用。
| 遥测 (telemetry) | 事件类型 | 在profile中启用 |
|---|---|---|
| 内存 | jdk.GCHeapSummary, jdk.MetaspaceSummary | 全部 |
| 已记录吞吐量 | jdk.ObjectAllocationSample, jdk.ObjectAllocationInNewTLAB, jdk.ObjectAllocationOutsideTLAB | 仅profile |
| GC活动 | jdk.GarbageCollection | 全部 |
| 类 | jdk.ClassLoadingStatistics | 全部 |
| 线程 | jdk.JavaThreadStatistics | 全部 |
| CPU负载 | jdk.CPULoad | 全部 |
内存视图
在“内存”部分,使用了两种不同的事件类型来填充视图数据。“活动对象”视图为你展示了在一次完整垃圾回收后仍然留在堆上的所有类及其实例数量的统计表示。只有在jdk.ObjectCount事件被启用时,这些数据才可用,而默认的JFR模板并未启用该事件,因为它会带来较大的开销。你也可以在高级JFR配置中通过“垃圾收集器”下拉框切换此设置。在Java
17之前,该下拉框标记为“内存分析”。
如果jdk.ObjectCount事件在快照中被记录了多次,视图将展示jdk.ObjectCount事件第一次和最后一次出现之间的差异。这样,你可以了解在记录期间这些数值的变化情况,并可能为内存泄漏提供一些线索。如果这些时间点与快照记录的起止点不一致,相应的书签会被添加到遥测
(telemetry)视图中。只有总对象大小超过固定阈值(通常为堆的1%)的类才会被包含。
如需更深入的分析,建议使用完整分析会话或获取HPROF快照。
“已记录对象”视图以及分配(allocation)视图展示的是自Java
16起jdk.ObjectAllocationSample事件的数据,以及早期Java版本中的jdk.ObjectAllocationInNewTLAB和jdk.ObjectAllocationOutsideTLAB事件。高级UI中的“分配分析”下拉框也可以启用这些事件类型。
与“活动对象”视图不同,这些视图只显示在记录期间被分配(allocated)的对象。JFR会对分配(allocation)进行采样,但大小仅作为总分配(allocated)大小的估算值进行报告。由于这种差异,这些视图中报告的大小并不等于采样次数乘以平均实例大小。除此之外,这些视图的功能与完整分析会话中的内存视图类似。
CPU视图
“CPU视图”包括调用树(call tree)、热点(hot spot)视图以及调用图(call
graph)。“Runnable”线程状态下的数据基于jdk.ExecutionSample事件,这些事件在标准JFR模板中默认被记录。然而,采样频率默认设置为20毫秒,对应于JFR高级UI中“方法采样”设置的“Normal”选项。由于JFR只对极少量的随机线程进行采样,因此收集到足够的数据以使热点(hot
spot)突出可能需要很长时间。如有需要,可以降低jdk.ExecutionSample的周期。请注意,这可能会导致快照文件非常大,因为JFR不会对数据进行累计。
由于线程是零星采样的,因此无法像完整分析会话那样估算实际执行时间。在调用树(call tree)和热点(hot spot)视图中显示的是事件计数而不是时间。这与异步采样(async sampling)类似,并有相同的局限性。JFR的其他线程状态包括“Waiting”、“Blocking”以及“Socket和文件I/O”,这些状态仍然以时间进行度量。由于这种差异,线程状态选择器中不提供“所有线程状态”模式。
另一个需要注意的是,非Runnable线程状态是根据事件计算的,这些事件有可配置的最小时长阈值,阈值会显示在线程状态选择器旁的工具提示中。这些线程状态的实际总时间可能会大得多。下表展示了用于组装线程状态的事件类型:
| 线程状态 | 事件类型 |
|---|---|
| Runnable | jdk.ExecutionSample |
| Waiting | jdk.JavaMonitorWait, jdk.ThreadSleep, jdk.ThreadPark |
| Blocking | jdk.JavaMonitorEnter |
| Socket和文件I/O | jdk.SocketRead, jdk.SocketWrite, jdk.FileRead, jdk.FileWrite |
各视图的功能详见CPU视图帮助主题。请注意,完整分析会话的许多功能在JFR上下文中不可用。
线程和monitor视图
通过按时间顺序的方法采样数据,可以计算出线程历史视图,包括显示等待和阻塞时间堆栈跟踪的工具提示。
线程转储(thread
dump)是JFR和JProfiler都支持的功能,并在同一视图中显示。在这种情况下,事件浏览器无法替代,因为它无法显示jdk.ThreadDump事件线程转储列的结构化内容。在线程转储视图中,你还可以比较不同的线程转储。
探针 (probe)
在完整分析会话中的某些JVM探针 (probe)在JFR快照中有等效的数据源。与事件浏览器相比,它们的主要优势在于可以组合多个相关事件类型。下表展示了可用的探针 (probe)及其数据源事件类型。
| 探针 (probe) | 事件类型 | 在profile中启用 |
|---|---|---|
| Socket | jdk.SocketRead, jdk.SocketWrite | 全部 |
| 文件 | jdk.FileRead, jdk.FileWrite | 全部 |
| 类 | jdk.ClassLoad, jdk.ClassUnload, jdk.ClassDefine | 无 |
| 异常 (exception) | jdk.JavaErrorThrow, jdk.JavaExceptionThrow | 两者中的错误,异常 (exception)均未启用 |
| 垃圾收集器 | jdk.GarbageCollection, jdk.GCPhasePause, jdk.YoungGarbageCollection, jdk.OldGarbageCollection, jdk.GCReferenceStatistics, jdk.GCPhasePauseLevel<n>, jdk.GCHeapSummary, jdk.MetaspaceSummary, jdk.GCHeapConfiguration, jdk.GCConfiguration, jdk.YoungGenerationConfiguration, jdk.GCSurvivorConfiguration, jdk.GCTLABConfiguration | 全部 |
类加载在高级JFR UI中有单独的复选框,可同时开启所有三种类加载事件。
每个探针 (probe)会显示多个视图。与事件浏览器不同,探针 (probe)更关注聚合数据而非单个事件。这也是JProfiler中的探针 (probe)在概念上与JFR数据收集的不同之处。
除了垃圾收集器探针 (probe)外,所有探针 (probe)都包含以下视图:调用树(call tree)和热点(hot spot)视图允许你选择单个线程或线程组,以及聚合级别(aggregation level)。默认情况下,显示所有线程,聚合级别(aggregation level)为“方法”。
遥测 (telemetry)视图会从已记录数据中显示一个或多个遥测 (telemetry),并有一个概览页面同时展示所有内容。点击遥测 (telemetry)名称可以打开完整遥测 (telemetry)。通过沿时间轴拖动,可以在事件视图中选择相应事件。
事件视图与JFR浏览器中的类似,但它会显示多个对应底层JFR事件的事件类型,并提供类型选择器。单选和多选的过滤与堆栈跟踪显示方式与事件浏览器一致。此外,还提供时间和内存测量的直方图视图,可以通过沿横轴拖动选择范围。
垃圾收集器视图比较特殊,因为完整分析会话在Java 17及以上版本中可以显示完全相同的信息。当JVM探针 (probe)类别下的垃圾收集器探针 (probe)被记录时,会使用JFR流式传输来获取所需数据。更多信息请参见垃圾收集器分析章节。















