JProfiler 是一款用于 Java 应用程序性能分析和优化的工具。它可以帮助开发人员识别性能瓶颈、内存泄漏等问题,并提供可视化的分析报告和建议。JProfiler 支持各种 Java 虚拟机、应用服务器和操作系统,可以在本地或远程环境中运行。下载安装完成后对应的说明文档在按照目录下的doc里边如图
自测使用的为11,激活码为“:L-J11-Everyone#speedzodiac-327a9wrs5dxvz#463a59
下面是翻译的中文文档
JProfiler的权威指南
索引
介紹 .....................................................................................................................................................................4
建筑学 .................................................................................................................................................................5
安装 .....................................................................................................................................................................7
剖析一个JVM ...................................................................................................................................................10
记录数据...........................................................................................................................................................22
快照...................................................................................................................................................................35
遥测数据 ...........................................................................................................................................................40
CPU剖析...........................................................................................................................................................45
内存剖析 .........................................................................................................................................................59
堆积步行者.......................................................................................................................................................68
线程剖析...........................................................................................................................................................84
探头...................................................................................................................................................................90
MBean浏览器 ................................................................................................................................................102
离线剖析.......................................................................................................................................................106
比较快照.........................................................................................................................................................111
集成开发环境整合..........................................................................................................................................117
A 定制探头 .....................................................................................................................................................125
A.1 探头概念..............................................................................................................................................125
A.2 脚本探针..............................................................................................................................................131
A.3 注射式探头..........................................................................................................................................135
A.4 嵌入式探头..........................................................................................................................................140
B 呼叫树功能详解 .........................................................................................................................................144
B.1 仪表的自动调谐...............................................................................................................................144
B.2 异步和远程请求跟踪..........................................................................................................................147
B.3 查看呼叫树的部分内容 ......................................................................................................................153
B.4 分割呼叫树..........................................................................................................................................158
B.5 呼叫树分析..........................................................................................................................................162
C 高级CPU分析视图.....................................................................................................................................167
C.1 方法统计..............................................................................................................................................167
C.2 复杂度分析..........................................................................................................................................171
C.3 呼叫追踪器..........................................................................................................................................173
C.4 脚本 XHR.............................................................................................................................................175
D 堆垛机的详细特征.....................................................................................................................................178
D.1 HPROF快照 .........................................................................................................................................178
D.2 最大限度地减少管理费用..................................................................................................................180
D.3 过滤器和现场互动..............................................................................................................................182
D.4 寻找内存泄漏......................................................................................................................................185
E 详细配置 .....................................................................................................................................................192
E.1 连接问题的故障排除 ..........................................................................................................................192
E.2 脚本 ......................................................................................................................................................193
F 命令行参考 .................................................................................................................................................196
F.1 用于分析的可执行程序.......................................................................................................................196
F.2 用于快照的可执行文件.......................................................................................................................199
F.3 Gradle任务...........................................................................................................................................208
F.4 蚂蚁的任务...........................................................................................................................................212
JProfiler简介
什么是JProfiler?
JProfiler是一个专业的工具,用于分析运行中的JVM内部正在发生的事情。你可以在开发中使用
它,以保证质量,并在你的生产系统遇到问题时进行消防任务。
JProfiler主要处理四个主题:
•
•
•
方法调用
这通常被称为 "CPU剖析"。方法调用可以通过不同的方式被测量和可视化。对方法调用的分
析有助于你了解你的应用程序正在做什么,并找到改善其性能的方法。
拨款
分析堆上对象的分配、引用链和垃圾回收属于 "内存分析 "的范畴。这种功能使你能够修复内
存泄漏,在一般情况下使用更少的内存并分配更少的临时对象。
螺纹和锁具
线程可以持有锁,例如通过对一个对象进行同步。当多个线程合作时,可能会出现死锁,
JProfiler可以为你可视化。另外,锁可以被争夺,这意味着线程在获得锁之前必须要等待。
JProfiler提供了对线程及其各种锁情况的洞察力。
•
更高层次的子系统
许多性能问题发生在更高的语义层面上。例如,对于JDBC调用,你可能想找出哪个SQL语句是最
慢的。对于这样的子系统,JProfiler提供了 "探针",将特定的有效载荷附加到调用树中。
JProfiler的用户界面是作为一个桌面应用程序提供的。你可以交互式地对一个实时的JVM进行剖析,
也可以不使用用户界面自动进行剖析。剖析数据被保存在快照中,可以用JProfiler用户界面打开
。
此外,命令行工具和构建工具的集成可以帮助你实现自动剖析会话。
我如何继续?
本文档旨在按顺序阅读,后面的帮助主题是建立在前面的内容之上的。
首先,对架构的技术概述[第5页]将帮助你理解剖析的工作原理。
关于安装JProfiler[第7页]和剖析JVM[第10页]的帮助主题将使你开始运行。
在此之后,关于数据记录[第22页]和快照[第35页]的讨论将你带入一个理解的层次,你可以自行
探索JProfiler。
随后的几章将建立你对JProfiler中不同功能的专业知识。最后的章节是可选读物,如果你需要某些功
能,应该查阅。
我们感谢你的反馈。如果你觉得在某个领域缺乏文件,或发现文件中的不准确之处,请不要犹
豫,与我们联系,support@ej-technologies.com。
JProfiler架构
下面给出了涉及被剖析的应用程序、JProfiler用户界面和所有命令行实用程序的所有重要交互的
大图片。
jpexport
jpcompare
jpanalyze
JProfiler用
户界面
键盘手
快照
远程或本地
当地
传递数据 通过插座连接
通过JMX
连接
通过附
件加载
JProfiler
代理
出版
JProfiler
MBean
ǞǞǞ
采取HPROF
堆转储
倾泻器(
jpdump
剖析的JVM
负荷与
-实验路径
带有离线剖析
的控制
命令行参数
加载剖析剂
命令行工具
过程部分
数据
控制录音
剖析数据
剖析剂
JVM工具接口"(JVMTI)是一个本地接口,剖析器用它来获取信息并添加钩子以插入自己的仪
"
器。这意味着,至少有一部分
的剖析代理必须作为本地代码实现,因此JVM剖析器不是独立于平台的。JProfiler支持一系列的
平台,这些平台在网站上列出(1) 。
JVM剖析器是作为一个本地库实现的,在启动时或稍后的某个时间点加载。要在启动时加载它
,
需要在命令行中添加一个VM参数-agentpath:<本地库的路径>。你很少需要手动添加这个
参数,因为JProfiler会帮你添加,例如在IDE集成、集成向导或直接启动JVM时。然而,重要的
是要知道,这是实现剖析的原因。
如果JVM成功加载了本地库,它就会调用库中的一个特殊函数,让剖析代理有机会初始化自己
。
然后,JProfiler将打印一些以JProfiler>为前缀的诊断信息,以便你知道剖析是活动的。底
线是,如果你传递了-agentpath VM参数,剖析代理要么被成功加载,要么JVM不启动。
一旦加载,剖析代理要求JVMTI得到各种事件的通知,如线程创建或类加载。其中一些事件直接提
供剖析数据。使用类加载事件,剖析代理在类被加载时对其进行检测,并插入自己的字节码以
进行测量。
JProfiler可以通过使用JProfiler用户界面,或使用bin/jpenable命令行工具,将代理加载到已经运
行的JVM中。在这种情况下,大量已经加载的类可能需要重新转换,以便应用所需的仪器。
记录数据
JProfiler代理只收集剖析数据。JProfiler用户界面是单独启动的,并通过套接字连接到剖析
代理。这意味着,被剖析的JVM是在本地机器上还是在远程机器上运行其实并不重要--剖析
代理和JProfiler UI之间的通信机制总是相同的。
从JProfiler用户界面,你可以指示代理记录数据,在用户界面中显示剖析数据并将快照保存到磁盘。
作为用户界面的替代品,剖析代理可以通过其MBean(2) 来控制。一个使用该MBean的命令行工具是
bin/jpcontroller。
然而,另一种控制剖析剂的方法是使用一组预定义的触发器和行动。通过这种方式,剖析代理可以
在无人值守的模式下运行。这在JProfiler中被称为 "离线剖析",对自动剖析会话很有用。
快照
虽然JProfiler用户界面可以显示实时剖析数据,但往往需要保存所有记录的剖析数据的快照。快
照可以在JProfiler用户界面中手动保存,也可以通过触发动作自动保存。
快照可以在JProfiler用户界面中打开和比较。对于自动处理,命令行工具bin/jpexport和
bin/jpcompare可以用来提取数据并从先前保存的快照中创建HTML报告。
从运行中的JVM获取堆快照的一种低开销方式是使用bin/jpdump命令行工具。它使用JVM的内
置功能来保存HPROF快照,可以被JProfiler打开,不需要加载剖析代理。
(1)
https://www.ej-technologies.com/products/jprofiler/featuresPlatforms.html
https://en.wikipedia.org/wiki/Java_Management_Extensions
(2)
安装JProfiler
为Windows和Linux/Unix提供可执行的安装程序,引导你一步步完成安装。如果检测到以前的安
装,安装将被精简。
在macOS上,JProfiler使用UI应用程序的标准安装程序:提供一个DMG档案,你可以在Finder
中双击它,然后你可以将JProfiler应用程序捆绑到/Applications文件夹中。该文件夹在DMG
本身中作为一个符号链接可见。
在Linux/Unix上,安装程序在下载后不能执行,所以你在执行时必须在前面加上sh。安装程序执
行的是命令行安装,如果你通过参数
-
c.对于Windows和Linux/Unix的完全无人值守的安装是通过参数-q进行的。在这种情况下,你
可以传递额外的参数-dir <目录>,以便选择安装目录。
在 你 运 行 一 个 安 装 程 序 后 , 它 将 保 存 一 个 包 含 所 有 用 户 输 入 的
.
.
install4j/response.varfile 文件。你可以通过在命令行中传递参数-varfile <响应
varfile的路径>,利用该文件来实现无人值守的自动安装。
要为无人值守的安装设置许可信息,请通过-Vjprofiler.licenseKey=
<
许 可 证 密 钥 > -Vjprofiler.licenseName=< 用 户 名 > 和 可 选 的 -
Vjprofiler.licenseCompany=<公司名称>作为命令行参数。如果你有一个浮动许可证,使用
FLOAT:<服务器名称或IP地址>代替许可证密钥。
档案也以ZIP文件形式提供给Windows,以.tar.gz文件形式提供给Linux。命令
tar xzvf filename.tar.gz
将提取一个.tar.gz归档文件到一个单独的顶层目录。要启动JProfiler,在提取的目录中执行bin/
jprofiler。在Linux/Unix上,文件jprofiler.desktop可以用来将JProfiler的可执行文件整合
到你的窗口管理器中。例如,在Ubuntu上,你可以将桌面文件拖入启动器侧边栏,以便创建一
个永久的启动器项目。
将剖析代理分发到远程机器上
JProfiler有两个部分:一方面是桌面UI和操作快照的命令行工具,另一方面是剖析代理和控制剖
析JVM的命令行工具。你从网站上下载的安装程序和档案包含这两部分。
然而,对于远程剖析,你只需要在远程端安装剖析代理。虽然你可以简单地在远程机器上提取
一个带有JProfiler分布的归档文件,但你可能想限制所需文件的数量,特别是在自动部署时。另外,
剖析代理是可以自由再分配的,所以你可以把它和你的应用程序一起发送,或者安装在客户机器上
进行故障排除。
为了获得带有剖析代理的最小包,远程集成向导提供了为任何支持的平台创建这样一个存档的
选项。在JProfiler GUI中,调用
在 步骤中选择
->
->
/
,选择 "远程 "选项,然后
。
如果有必要,JProfiler将下载所需的本地代理库以及jpenable、jpdump和jpcontroller可执行
文件,并根据目标平台创建一个.tar.gz或.zip的存档。归档文件中的所有上述可执行文件只需要Java
6的最低版本,而剖析代理可在Java 5或更高版本中工作。
你在远程机器上解压后看到的子目录描述如下。它们是在各自目标平台上完整的JProfiler安装的
一个子集。
提取后的顶层目录
.install4j
启动器的运行时间
bin
代理人JAR文件和帮助执行程序的本地库,
用于64位JVMs
<平台-64
<
<平台-32>>
用于32位JVM的本地库
支持附加功能的库
枰子
剖析一个 JVM
为了对JVM进行剖析,JProfiler的剖析代理必须被加载到JVM中。这可以通过两种不同的方式实
现:通过在启动脚本中指定一个-agentpath VM参数,或者使用attach API将代理加载到一个
已经运行的JVM中。
JProfiler支持这两种模式。添加虚拟机参数是首选的剖析方式,并被集成向导、IDE插件和从
JProfiler内启动JVM的会话配置所使用。附加在本地以及通过SSH远程工作。
-agentpath VM参数
了解加载剖析代理的虚拟机参数是如何组成的是很有用的。
-
agentpath是一个通用的VM参数,由JVM提供,用于加载任何种类的使用JVMTI接口的本地库。
因为剖析接口JVMTI是一个本地接口,剖析代理必须是一个本地库。这意味着,你只能在明确的
支持的平台(1) 。32位和64位JVM也需要不同的本地库。另一方面,Java代理是用-javaagent
VM参数加载的,只能访问一组有限的能力。
在 -agentpath:之后,本地库的完整路径名称被附加。有一个相等的参数 -agentlib:,你
只指定特定平台的库名,但你必须确保该库包含在库路径中。在库的路径之后,你可以添加一
个等号,并向代理传递选项,用逗号分隔。例如,在Linux上,整个参数可以是这样的:
-
agentpath:/opt/jprofiler10/bin/linux-x64/libjprofilerti.so=port=8849,nowait
第一个等号将路径名称与参数分开,第二个等号是参数port=8849的一部分。这个常用参数定
义了剖析代理从JProfiler GUI监听连接的端口。8849实际上是默认端口,所以你也可以省略这
个参数。如果你想在同一台机器上对多个JVM进行剖析,你必须指定不同的端口。IDE插件和本
地启动的会话会自动分配这个端口,对于集成向导,你必须明确选择端口。
第二个参数nowait告诉剖析代理在启动时不要阻塞JVM,而要等待JProfiler GUI的连接。启动时阻
断是默认的,因为剖析代理不以命令行参数的形式接收其剖析设置,而是从JProfiler GUI或者
从一个配置文件中接收。命令行参数仅用于引导剖析代理,告诉它如何启动和传递调试标
志。
默认情况下,JProfiler代理将通信套接字绑定到所有可用的网络接口。如果出于安全原因,这并
不可取,你可以添加选项address=[IP地址],以选择一个特定的接口。
当地发起的会议
像IDE中的 "运行配置 "一样,你可以在JProfiler中直接配置本地启动的会话。你指定类的路径、主
类、工作目录、虚拟机参数和参数,JProfiler为你启动会话。所有随JProfiler而来的演示会话都
是本地启动的会话。
(1)
https://www.ej-technologies.com/products/jprofiler/featuresPlatforms.html
一个特殊的启动模式是 "网络启动",你选择JNLP文件的URL,JProfiler将启动一个JVM对其进
行剖析。
通过从主菜单调用
->
可以用转换向导将本地启动的会话转换为独立的会话。
只需创建一个启动脚本,并将-agentpath VM参数插入到Java调用中。将
创建一个用于脱机剖析的启动脚本 [第 106 页],这意味着
转换
配置在启动时被加载,并且不需要JProfiler GUI。
做同样
的事情,但在其旁边创建一个目录jprofiler_redist,其中包含剖析代理以及配置文件,因
此你可以将其运送到未安装JProfiler的不同机器。
如果你自己开发被剖析的应用程序,考虑使用IDE集成[第117页]而不是启动会话。这将更方便
,
并给你更好的源代码导航。如果你不自己开发应用程序,但已经有一个启动脚本,考虑使用
远程集成向导。它将告诉你必须添加到Java调用中的确切的VM参数。
集成向导
JProfiler的集成向导处理许多著名的第三方容器的启动脚本或配置文件,可以通过编程方式修改
,
以包括额外的虚拟机参数。对于一些产品,可以生成启动脚本,其中虚拟机参数作为参数或
通过环境变量传递。
在所有情况下,你必须从第三方产品中找到一些特定的文件,这样JProfiler就有必要的上下文来
执行其修改。一些通用的向导只给你指示,你必须做什么才能启用剖析。
每个集成向导的第一步是选择是在本地机器上还是在远程机器上进行配置。在本地机器的情况下
,你必须提供较少的信息,因为JProfiler已经知道平台,JProfiler的安装位置和它的配置文件的位
置。
一个重要的决定是上面讨论过的 "启动模式"。默认情况下,剖析设置会在启动时从JProfiler UI
中传输,但你也可以告诉剖析代理让JVM立即启动。在后一种情况下,一旦JProfiler GUI连接,
剖析设置就可以应用。
然而,你也可以指定一个带有剖析设置的配置文件,这就更有效了。这是在配置同步步骤中完成的
。
这种情况下的主要问题是,每次你在本地编辑剖析设置时,都必须将配置文件与远程端同步。最
优雅的方法是在远程地址步骤中通过SSH连接到远程机器,然后配置文件可以通过SSH自动传输
。
在集成向导的最后,将创建一个会话,开始分析,并且--在非一般情况下--也启动第三方产品,
如应用服务器。
外部启动脚本由会话配置对话框的
处理,URL可以通过选择 URL
置同步选项的地方。
选项卡上的
和
选项
复选框显示。这也是你可以改变远程机器的地址和配
集成向导都能处理被剖析的JVM在远程机器上运行的情况。然而,当需要修改配置文件或启动脚本
时,你必须将其复制到本地机器上,并将修改后的版本传送回远程机器上。直接在远程机器上运行
命令行工具jpintegrate,让它在原地进行修改可能会更方便。
当启动远程剖析会话时发生错误,请参阅故障排除指南[第192页],了解你可以采取的步骤的检
查清单,以解决这个问题。
集成开发环境的整合
对应用程序进行剖析的最方便的方法是通过IDE的集成。如果你在开发过程中通常从你的IDE启
动你的应用程序,IDE已经有了所有需要的信息,JProfiler插件可以简单地添加用于剖析的VM参
数,必要时启动JProfiler,并将剖析的JVM连接到JProfiler的主窗口。
所有的IDE集成都包含在JProfiler安装的集成目录中。原则上,该目录中的档案可以通过各自
IDE的插件安装机制手动安装。然而,安装IDE集成的首选方式是在主菜单中调用
。
->IDE
来自IDE的剖析会话在JProfiler中没有自己的会话条目,因为这样的会话不能从JProfiler GUI中启动
。剖析设置在每个项目或每个运行配置的基础上被持久化,这取决于IDE中的设置。
当连接到IDE时,JProfiler在工具条上显示一个窗口切换器,使其能够轻松跳回IDE中的相关窗
口。所有
。
操作现在都直接在IDE中显示源码,而不是在JProfiler中的内置源码查看器
集成开发环境的整合将在后面一章中详细讨论[第117页]。
附加模式
你不一定要事先决定你打算对JVM进行剖析。通过JProfiler的附加功能,你可以选择一个正在运
行的JVM,并在运行中加载剖析代理。虽然附加模式很方便,但它有几个缺点,你应该注意到:
•
你必须从正在运行的JVM列表中确定你要剖析的JVM。如果同一台机器上有很多JVM在运行
,这有时会很麻烦。
•
•
有额外的开销,因为可能要重新定义许多类来添加仪器。
JProfiler中的一些功能在附加模式下是不可用的。这主要是因为JVMTI的一些功能只能在JVM
初始化时开启,在JVM生命周期的后期阶段无法使用。
•
有些功能需要在很大一部分的类中设置仪器。在类被加载的时候进行检测是很便宜的,而在后来
类已经被加载的时候再添加检测就不便宜了。当你使用附加模式时,这类功能默认是禁用的
。
•
附加功能只支持版本6或更高的Oracle JVM。
JProfiler启动中心的
标签列出了所有可以进行剖析的JVM。列表项的背景颜色表示是否
已经加载了剖析代理,是否当前连接了JProfiler GUI,或者是否已经配置了离线剖析。
当你启动一个剖析会话时,你可以在会话设置对话框中配置剖析设置。当你重复对同一过程进行
剖析时,你不想一次又一次地重新输入相同的配置,所以当你关闭一个用快速附加功能创建的会
话时,可以保存一个持久的会话。下次你想对这个过程进行剖析时,从 "
"选项卡而不是
"
"选项卡启动保存的会话。你仍然需要选择一个正在运行的JVM,但是剖析设置是你
之前已经配置好的那些。
依附于当地的服务
JVM中的attach API要求调用的进程与你想附加的进程以相同的用户身份运行,所以JProfiler显示
的JVM列表仅限于当前用户。由不同用户启动的进程大多是服务。在Windows和基于Unix的平
台上,附加到服务的方式是不同的。
在Windows上,附加对话框有一个
按钮,列出所有本地运行的服务。JProfiler启动桥
接可执行文件,以便能够附加到这些进程,无论它们以何种用户运行。
在基于Unix的平台上,包括macOS,你可以用su或sudo作为不同的用户执行命令行工具
jpenable,这取决于你的Unix变体或Linux发行版。在macOS和基于Debian的Linux发行版,
如Ubuntu,使用sudo。
使用sudo,调用
sudo -u userName jpenable
使用su,需要的命令行是
su userName -c jpenable
jpenable会让你选择JVM,并告诉你剖析代理正在监听的端口。在启动中心的 "
"选
项卡上,你可以选择 "
"选项,并配置一个直接连接到localhost和指定的剖析
端口。
附加到远程机器上的JVM
对剖析要求最高的设置是远程剖析--JProfiler GUI在你的本地机器上运行,而被剖析的JVM在另一台
机器上。对于将-agentpath VM参数传递给被剖析的JVM的设置,你必须在远程机器上安装JProfiler
,
并在本地机器上设置一个远程会话。有了JProfiler的远程附加功能,就不需要做这样的修改,你只
需要用SSH凭证来登录远程机器。
SSH连接使JProfiler能够上传 "安装JProfiler"[第7页]帮助主题中讨论的代理包,并在远程机器上
执行包含的命令行工具。你不需要在你的本地机器上设置SSH,JProfiler有它自己的实现。在最
简单的设置中,你只需定义主机、用户名和认证。
通过SSH连接,JProfiler可以执行自动发现正在运行的JVM,或者连接到一个已经在监听的剖析代
理的特定端口。对于后一种情况,你可以在远程机器上使用jpenable或jpintegrate,如上
所述,并准备一个特殊的JVM用于剖析。然后,可以将SSH远程附加配置为直接连接到配置的
剖析端口。
自动发现将列出远程机器上所有已经以SSH登录用户身份启动的JVM。在大多数情况下,这不
会是你想剖析的那个启动服务的用户。因为启动服务的用户通常不允许进行SSH连接,JProfiler增
加了一个
按钮,让你使用sudo或su来切换到该用户。
在复杂的网络拓扑结构中,你有时无法直接连接到远程机器。在这种情况下,你可以告诉
JProfiler在GUI中使用多跳SSH隧道进行连接。在SSH隧道的末端,你可以进行一个直接的网络
连接,通常是连接到 "127.0.0.1"。
HPROF快照只能用于以SSH登录用户启动的JVM。这是因为HPROF快照需要一个中间文件,
该文件是以启动JVM的用户的访问权限写入的。出于安全原因,不可能将文件权限转移给SSH
登录用户进行下载。对于完全剖析会话不存在这样的限制。
设置运行中的JVM的显示名称
在JVM选择表中,显示的进程名称是被剖析的JVM的主类以及其参数。对于由exe4j或install4j生
成的启动器,显示的是可执行名称。
如果你希望自己设置显示的名称,例如因为你有几个具有相同主类的进程,否则将无法区分,你可
以设置VM参数-Djprofiler.displayName=[name]。如果名称包含空格,请使用单引号:
-
Djprofiler.displayName='我的名字加空格',必要时用双引号引用整个VM参数。除了-
Djprofiler.displayName之外,JProfiler还能识别-Dvisualvm.display.name。
记录 数据
剖析器的主要目的是记录来自不同来源的运行时数据,这些数据对解决常见的问题很有用。这
项任务的主要问题是,运行中的JVM会以巨大的速度生成这些数据。如果剖析器总是记录所有
类型的数据,就会产生不可接受的开销,或者迅速耗尽所有可用的内存。另外,你经常想围绕
一个特定的用例来记录数据,而不是看到任何不相关的活动。
这就是为什么JProfiler提供细粒度的机制来控制你真正感兴趣的信息的记录。
标量值和遥测数据
从剖析器的角度来看,最没有问题的数据形式是标量值,例如活动线程的数量或打开的JDBC连
接的数量。JProfiler可以以固定的宏观频率(通常是每秒一次)对这类数值进行采样,并向你展示
随时间的变化。在JProfiler中,显示此类数据的视图被称为遥测数据[第40页]。大多数遥测数据
总是被记录下来,因为测量的开销和内存消耗都很小。如果数据被记录了很长时间,旧的数据
点会被合并,这样内存消耗就不会随时间线性增长。
还有一些参数化的遥测数据,如每个类别的实例数量。额外的维度使得永久性的按时间顺序记录
是不可持续的。你可以告诉JProfiler记录一些选定类的实例数的遥测数据,但不是每一个类的实
例数。
继续前面的例子,JProfiler能够向你显示所有类的实例数,但没有时间上的信息。这就是 "所有对
象 "视图,它将每个类显示为表格中的一行。更新该视图的频率低于每秒一次,并且可以根据测
量引起的开销大小自动调整。确定所有类的实例计数是相对昂贵的,而且堆上的对象越多,花
费的时间越长。JProfiler限制了 "所有对象 "视图的更新频率,以便在极端情况下测量的开销不
会超过10%。你可以冻结视图以暂时停止记录。另外,如果视图没有被激活,数据将不会被记
录,也没有相关的开销。
一些测量方法捕获了类似枚举的值,例如线程当前所处的执行状态。这种测量可以显示为彩色的
时间线,并且比数字遥测消耗的内存少很多。在线程状态的情况下,"线程历史 "视图显示JVM
中所有线程的时间线。就像数字遥测一样,旧的数值被合并,变得更加粗略,以减少内存消耗
。
拨款记录
如果你对某一时间间隔内分配的实例数感兴趣,JProfiler必须跟踪所有的分配。与 "所有对象 "
视图相反,JProfiler可以遍历堆中的所有对象来获取需求信息,跟踪单个分配需要为每个对象分
配执行额外的代码。这使得它成为一个非常昂贵的测量方法,可以显著改变被剖析应用程序的运
行时特性,例如性能热点,特别是当你分配了许多对象时。这就是为什么分配记录必须明确地
开始和停止。
有相关录音的视图最初会显示一个带有录音按钮的空页。同样的录音按钮也可以在工具栏中找
到。
分配记录不仅记录了分配实例的数量,还记录了分配的堆栈痕迹。在内存中保留每个分配记录的
堆栈痕迹会产生过多的开销,所以JProfiler将记录的堆栈痕迹累积到一个树中。这也有一个好处
,
就是你可以更容易地解释这些数据。然而,按时间顺序排列的方面被丢失了,而且没有办法
从数据中提取某些时间范围。
内存分析
分配记录只能测量对象被分配的位置,没有对象之间的引用信息。任何需要引用的内存分析,
例如解决内存泄漏,都是在堆漫步器中完成的。堆漫步器对整个堆进行快照并分析。这是一个
侵入性的操作,会使JVM暂停--可能会暂停很长时间--并且需要大量的内存。
一个更轻量级的操作是在你开始一个用例之前标记堆上的所有对象,这样你就可以在以后进行
堆快照时找到所有新分配的对象。
JVM有一个特殊的触发器,用于将整个堆转储到一个文件,该文件是以旧的HPROF剖析代理命
名的。这与剖析接口无关,不在其约束下运行。由于这个原因,HPROF的堆转储速度更快,使
用的资源更少。缺点是,在堆漫步器中查看堆快照时,你将没有一个与JVM的实时连接,而且
一些功能是不可用的。
呼叫记录的方法
测量方法调用的时间是一个可选的记录,就像分配记录一样。方法调用被累积成一棵树,并有
各种视图从不同角度显示记录的数据,如调用图。这类数据的记录在JProfiler中被称为 "CPU记
录"。
在特殊情况下,查看方法调用的时间顺序可能很有用,特别是在涉及多个线程的情况下。对于这
些特殊情况,JProfiler提供了 "Call tracer "视图。该视图有一个单独的记录类型,不与更一般的
CPU记录相联系。请注意,调用跟踪器产生的数据太多,对解决性能问题没有用处,它只用于
专门的调试形式。
另一个分析方法调用的视图是 "方法统计 "视图。它引入了另一个测量轴,并记录了每个方法的
执行时间的直方图。除非你对调查某些方法的执行时间是否存在异常值感兴趣,否则你不需要
这些数据,而且其记录会产生不可接受的开销。因此,有一个单独的方法统计记录,你必须在
相关视图中打开。
呼叫跟踪器和方法统计都依赖于CPU记录,并在必要时自动打开它。
另一个有自己记录的专门视图是 "复杂度分析"。它只测量选定方法的执行时间,不要求启用CPU
记录。它的额外数据轴是一个方法调用的算法复杂性的数值,你可以用脚本来计算。通过这种
方式,你可以测量一个方法的执行时间如何取决于其参数。
监控记录
为了分析线程等待或阻塞的原因,必须记录相应的事件。这类事件的发生率差别很大。对于一
个多线程程序,线程经常协调任务或共享公共资源,可能会有大量的此类事件。这就是为什么这
种按时间顺序排列的数据在默认情况下不被记录。
当你打开监视器记录时,"锁定历史图 "和 "监视器历史 "视图将开始显示数据。
为了消除噪音和减少内存消耗,非常短的事件不被记录。视图设置为您提供了调整这些阈值的
可能性。
探针记录
探针显示JVM中更高层次的子系统,如JDBC调用或文件操作。默认情况下,没有探针被记录,你
可以为每个探针单独切换记录。有些探针会增加很少或没有开销,有些则会产生相当多的数据
,
这取决于你的应用程序正在做什么和探针的配置方式。
就像分配记录和方法调用记录一样,探针数据是累积的,除了时间线和遥测数据外,按时间顺序
的信息被丢弃。然而,大多数探针也有一个 "事件 "视图,允许你检查单个事件。这增加了潜在的
大量开销,并有一个单独的记录动作。该记录动作的状态是持久的,因此,当你切换探头记录时
,
如果你之前已经打开了它,相关的事件记录也会被切换。
JDBC探针有第三个记录动作,用于记录JDBC连接泄漏。寻找连接泄漏的相关开销只有在你真正
试图调查时才会产生
这样的问题。就像事件记录动作一样,泄漏记录动作的选择状态是持久的。
录音简介
在许多情况下,你想通过一次点击来启动或停止各种录音。访问所有相应的视图并逐一切换录
音按钮是不现实的。这就是为什么JProfiler有录音配置文件。通过点击工具条上的
按钮
,
可以创建录音配置文件。
录音配置文件定义了一个特定的录音组合,可以被原子化地激活。JProfiler试图给你一个粗略的
印象,即你通过选定的记录创造的开销,并试图阻止有问题的组合。特别是,分配记录和CPU
记录并不相配,因为CPU数据的时序会被分配记录严重扭曲。
你可以在会话运行的任何时候激活录音配置文件。录音配置文件不是附加的,它们会停止所有
不包括在录音配置文件中的录音。用 "
"按钮可以停止所有的录音,无论它们是如何被
激活的。要检查当前有哪些录音是激活的,将鼠标悬停在状态栏的录音标签上。
当你开始进行剖析时,也可以直接激活一个录音预案。"会话启动 "对话框有一个
下拉框。默认情况下,没有选择记录预案,但是如果您需要JVM启动阶段的数据,这就是配置
所需记录的地方。
用触发器录音
有时你想在某个特定条件发生时开始录音。JProfiler有一个定义触发器的系统[第106页],可以
执行一系列的动作。可用的触发器动作也包括对活动录音的改变。
例如,你可能想只在某个特定方法被执行时才开始录音。在这种情况下,你可以进入会话设置对话
框,激活触发器设置选项卡,为该方法定义一个方法触发器。对于动作配置,你有许多不同的
录音动作可用。
开始录音 "动作控制那些没有任何参数的录音。通常,当你停止并重新启动一个记录时,所有以
前记录的数据都会被清除掉。对于 "CPU数据 "和 "分配数据 "的记录,你也可以选择保留以前的
数据,继续在多个间隔内累积。
通过使用上下文菜单中的 "添加方法触发器 "操作,可以在调用树中方便地添加方法触发器。如
果你在同一个会话中已经有一个方法触发器,你可以选择在现有的触发器上添加一个方法拦截
。
默认情况下,当JVM被启动进行剖析时,触发器是激活的。有两种方法可以在启动时禁用触发
器:你可以在触发器配置中单独禁用它们,或者在会话启动对话框中取消选择 "
"
复选框。在实时会话中,你可以从菜单中选择
->
|
来启用或禁用所
有触发器,或者
点击状态栏中的 触发记录状态图标。
有时,你需要同时切换触发器组的激活。这可以通过为感兴趣的触发器分配相同的组ID,并从
菜单中调用Profiling->Enable Triggers Groups 实现。
用jpcontroller录音
JProfiler有一个命令行可执行程序,用于控制任何已经被剖析的JVM中的记录。jpcontroller要求
JProfiler MBean已经发布,否则它将无法连接到被剖析的JVM。只有在剖析代理已经收到剖
析设置的情况下才会出现这种情况。如果没有剖析设置,代理将不知道具体要记录什么。
必须符合以下条件之一:
•
•
你已经用JProfiler GUI连接到JVM了
被剖析的JVM是用-agentpath VM参数启动的,其中包括nowait和config参数。在集
成向导中,这与
步骤中的
启动模式和在
选项相对应。
•
JVM准备用jpenable可执行文件进行剖析,并且指定了-offline参数。更多信息请参见
jpenable -help的输出。
具体来说,如果被剖析的JVM只用nowait标志启动,jpcontroller将无法工作。在集成向导中,
当与JProfiler GUI连接时, 步骤中的Apply configuration选项将配置这样一个参数。
jpcontroller为你提供了一个循环的多级菜单,用于所有的录音及其参数。你还可以用它保存快
照。
开始录音的程序化方式
然 而 , 另 一 种 开 始 录 音 的 方 式 是 通 过 API 。 在 剖 析 的 虚 拟 机 中 , 你 可 以 调 用
com.jprofiler.api.controller.Controller类,以编程方式开始和停止录音。更多信息以
及如何获得包含控制器类的工件,请参见离线剖析一章[第106页]。
如果你想在不同的JVM中控制录音,你可以在被剖析的JVM中访问同样的MBean,该MBean也被
jpcontroller使用。设置MBean的编程用法有些复杂,需要相当多的仪式,所以JProfiler提供了
一
个
例
子
,
你
可
以
重
新
使
用
。
查
看
文
件
api/samples/mbean/src/MBeanProgrammaticAccessExample. java。它在另一个被剖析
的JVM中记录了5秒钟的CPU数据,并将快照保存到磁盘。
快照
到目前为止,我们只看了实时会话,JProfiler GUI从运行在被剖析的JVM内的剖析代理那里获得
数据。JProfiler也支持快照,所有的剖析数据都被写入一个文件。这在一些情况下是有优势的:
•
•
•
你自动记录剖析数据,例如作为测试的一部分,这样就不可能与JProfiler GUI连接。
你想比较不同剖析时段的剖析数据,或查看旧的记录。
你想与别人分享剖析数据。
快照包括所有记录的数据,包括堆快照。为了节省磁盘空间,快照是被压缩的,除了必须保持
未压缩的堆行者数据,以便于直接进行内存映射。
在JProfiler GUI中保存和打开快照
当你正在对一个实时会话进行剖析时,你可以使用
工具条按钮来创建快照。JProfiler从
远程代理中提取所有的剖析数据,并将其保存到一个扩展名为".jps "的本地文件。在一个实时会
话的过程中,你可以保存多个这样的快照。它们不会被自动打开,你可以继续进行剖析。
保存的快照会自动添加到 "
->
"菜单中,因此你可以方便地打开你刚刚保存的快
照。当实时会话仍在运行时打开快照,你可以选择终止实时会话或打开另一个JProfiler窗口。
当你使用JProfiler中的快照比较功能时,快照列表中会出现你为当前实时会话保存的所有快照。
这使得比较不同的用例变得很容易。
一般来说,你可以通过从主菜单调用会话->打开快照或在文件管理器中双击快照文件来打开快照。
JProfiler的IDE集成也支持通过IDE本身的通用打开文件动作打开JProfiler快照。在这种情况下,你可
以在IDE中获得源代码导航,而不是内置的源代码查看器。
当你打开一个快照时,所有的记录动作都被禁用,只有带有记录数据的视图可用。要发现什么
样的数据已被记录,将鼠标悬停在状态栏的记录标签上。
剖析短命的程序
对于一个实时会话,所有的剖析数据都驻留在被剖析的JVM的进程中。因此,当被剖析的JVM被终
止时,JProfiler中的剖析会话也被关闭。为了在JVM退出时继续进行剖析,你有两个选项,都可
以在会话启动对话框中激活。
•
•
你可以防止JVM实际退出,只要JProfiler GUI被连接,就可以人为地保持它的活力。当你从IDE
中对一个测试用例进行剖析,并希望在IDE的测试控制台中看到状态和总时间时,这可能是不
可取的。
你可以要求JProfiler在JVM终止时保存一个快照,并立即切换到它。该快照是临时的,除非你先
使用
动作,否则在你关闭会话时将被丢弃。
用触发器保存快照
自动剖析会话的最终结果总是一个快照或一系列的快照。在触发器中,你可以添加一个 "保存快
照 "动作,在被剖析的JVM运行的机器上保存快照。当触发器在实时会话中运行时,快照也被保
存在远程机器上,并且可能不包括已经传输到JProfiler GUI的部分数据。
用触发器保存快照有两种基本策略:
•
•
对于测试案例,在 "JVM启动 "触发器中开始记录,并添加一个 "JVM退出 "触发器,以便在
JVM终止时保存快照。
对于像 "CPU负载阈值 "触发器这样的特殊情况,或者用 "定时器触发器 "进行定期剖析,在记
录一些数据后保存快照,中间有一个 "睡眠 "动作。
HPROF堆快照
在采取堆快照产生太多开销或消耗太多内存的情况下,你可以使用JVM提供的HPROF堆快照作为一
个内置功能。因为这个操作不需要剖析代理,这对于分析在生产中运行的JVM的内存问题很
有意义。
使用JProfiler,有三种方法可以获得这种快照:
•
对于实时会话,JProfiler GUI在主菜单中提供了一个动作来触发HPROF堆转储。
•
JProfiler有一个特殊的 "Out of memoryexception "触发器,当OutOfMemoryError被抛出时
,
保存一个HPROF快照。这对应于VM参数(1)
-XX:+HeapDumpOnOutOfMemoryError
这是由HotSpot JVM支持的。
(1)
Command-Line Options
•
JDK(2) 中的jmap可执行文件可以用来从运行中的JVM中提取HPROF堆转储。
JProfiler包括一个命令行工具jpdump,它比jmap的功能更全面。它可以让你选择一个进程,
可以连接到在Windows上作为服务运行的进程,对 混 合 的 32位 /64位 JVM没有问题, 并 对
HPROF快照文件自动编号。执行它的方法是
-
help选项获取更多信息。
(2)
http://download.oracle.com/javase/6/docs/technotes/tools/share/jmap.html
遥测数据
剖析的一个方面是随着时间的推移监测标量测量,例如使用的堆大小。在JProfiler中,这种图形
被称为遥测。观察遥测数据可以让你更好地了解被剖析的软件,让你在不同的测量中关联重要
的事件,如果你注意到意外的行为,可能会促使你用JProfiler中的其他视图进行更深入的分析。
标准的遥测数据
在JProfiler用户界面的 "VM遥测 "部分,一些遥测数据被默认记录。对于交互式会话,它们总是
被启用,你不必开始或停止它们的记录。
为了比较同一时间轴上的多个遥测数据,概览以可配置的行高显示多个小规模的遥测数据在彼
此之上。点击遥测标题可激活完整的遥测视图。
完整视图显示了一个带有当前值的图例,并且可能比概览中可见的有更多选项。例如,"内存 "
遥测允许你选择单个内存池。
探头也发布遥测数据。这些遥测数据不包括在 "遥测数据 "视图部分,但属于相应探针的 "遥测
数据 "标签的一部分。这些遥测数据的记录与它们的父探头的记录相联系。
最后,还有一些 "跟踪 "遥测,监测在另一个视图中被选中的标量值。例如,类跟踪器视图允许
你选择一个类,并随时间监测其实例数。另外,每个探针都有一个 "跟踪 "视图,在那里监测选
定的热点或控制对象。
书签
JProfiler维护一个书签列表,该列表显示在所有遥测数据中。在交互式会话中,你可以通过点击
工具条上的按钮,或使用上下文菜单中的在
功能,在当前时间添加一个书
签。
书签不仅可以手动创建,还可以由录音动作自动添加,以指示特定录音的开始和结束。通过触
发器动作或控制器API,你可以以编程方式添加书签。
书签有颜色,有线条样式,也有在工具提示中显示的名称。你可以编辑现有的书签并改变这些
属性。
如果右键点击遥测中的几个书签太不方便,你可以使用菜单中的Profiling->Edit Bookmarks动作来
获得一个书签列表。这也是你可以将书签导出为HTML或CSV的地方。
定制的遥测数据
有两种方法来添加你自己的遥测数据:要么你在JProfiler用户界面中写一个脚本来提供一个数字
值,要么你选择一个数字MBean属性。
要添加一个自定义遥测,请点击 "遥测 "部分中可见的
你可以访问当前JProfiler会话的classpath中配置的所有类。如果一个值不能直接使用,请给
你的应用程序添加一个静态方法,你可以在这个脚本中调用。
工具条按钮。在一个脚本遥测中
,
上面的例子显示了对一个平台MBean的调用。用MBean遥测来绘制MBean的标量值更为方便。这
里,一个MBean浏览器允许你选择一个合适的属性。该属性值必须是数字的。
你可以把几条遥测线捆绑成一个遥测。这就是为什么配置被分成两部分:遥测本身和遥测行。在遥
测行中,你只需编辑数据源和行的标题,在遥测中你可以配置适用于所有包含行的单位、比例和堆
叠。
在叠加遥测中,单条遥测线是相加的,可以显示一个面积图。比例因子对于将一个值转换为支
持的单位是很有用的。例如,如果数据源报告的是kB,问题是JProfiler中没有匹配的 "kB "单位
。
如果你把比例因子设置为-3,这些值将被转换为字节,通过选择 "字节 "作为遥测单位,
JProfiler将自动在遥测中显示适当的总量单位。
自定义遥测数据按照配置的顺序显示在 "遥测数据 "部分的末尾。
间接费用的考虑
乍一看,遥测数据似乎是随着时间的推移线性地消耗内存。然而,JProfiler整合了较早的数值,
并使其逐渐变得更加粗略,以限制每个遥测数据所消耗的总内存量。
遥测的CPU开销是有限的,因为它们的值每秒钟只被轮询一次。对于标准遥测,这种数据收集
没有额外的开销。对于自定义遥测,其开销受底层脚本或MBean的制约。
CPU 剖析
当JProfiler测量方法调用及其调用栈的执行时间时,我们称之为 "CPU分析"。这种数据有多种呈现方
式。根据你要解决的问题,一种或另一种展示方式将是最有帮助的。CPU数据默认是不被记录的,
你必须打开CPU记录[第22页]来捕捉有趣的用例。
采样与仪器的关系
测量方法调用可以通过两种不同的技术来完成,即 "采样 "和 "仪器化",每一种都有优点和
缺点:通过采样,JVM会定期停止,并检查当前的调用栈。通过仪器化,所选类的字节码被修改
以追踪方法的进入和退出。
在处理采样数据时,要对后续的采样进行比较。它们的共同调用栈显示了在两个样本之间的整
个时间里,哪个方法可能在执行。有了大量的样本,就会出现一个统计上正确的画面。采样的
优点是开销很低。不需要修改字节码,而且采样周期远远大于方法调用的典型时间。缺点是你
会错过短期的方法调用,而且你无法确定方法调用的数量。如果你在寻找性能瓶颈,这并不重
要,但如果你想了解你的代码的详细运行时特性,这就会很不方便。
方法A
方法B
方法X
=
=
方法A
方法B
方法Y
方法A:+5毫秒
方法B:+5毫秒
|
=
TT + 5 ms
时间
另一方面,如果许多短时运行的方法被仪器化,就会引入大量的开销。这种仪器化扭曲了性能
热点的相对重要性,因为时间测量的固有开销,但也因为许多本来会被热点编译器内联的方法现
在必须保持独立的方法调用。对于那些耗时较长的方法调用来说,开销是微不足道的。如果你能找
到一组主要进行高层操作的好类,仪器化会增加很低的开销,可以说是比取样更可取。另外,
调用次数往往是重要的信息,可以更容易地看到正在发生的事情。
剖析剂
X: 3.5 ms
呼叫
Y: 4.5 ms
呼叫
方法B:11毫秒
呼叫
方法A
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15
时间(毫秒
呼叫树
追踪所有的方法调用和它们的调用栈会消耗相当多的内存,而且只能保持很短的时间,直到所
有的内存都用完。另外,要直观地掌握一个繁忙的JVM中方法调用的数量并不容易。通常情况
下,这个数字是如此之大,以至于定位和跟踪痕迹是不可能的。
另一个方面是,许多性能问题只有在收集的数据被汇总后才会变得清晰。通过这种方式,你可
以知道方法调用在某个时间段内对整个活动有多重要。如果是单一的跟踪,你就无法知道你所看的
数据的相对重要性。
这就是为什么JProfiler建立了一个所有观察到的调用栈的累积树,并注释了观察到的时间和调用
次数。时间方面被消除了,只保留总的数字。树上的每个节点代表一个至少被观察过一次的调
用栈。节点有子节点,代表在该调用栈中看到的所有外发调用。
A
B
A
C
A
B
A
A: 7 ms
C: 1 ms
B: 6 ms
C: 3 ms
D: 1 ms
B
D
C
2
毫秒
1毫秒
3 ms
1毫
秒
带有调用堆栈的方法调用
调用树
调用树是 "CPU视图 "部分的第一个视图,当你开始进行CPU剖析时,它是一个很好的起点,因为从
起点到最细的终点的自上而下的视图最容易理解。JProfiler按照子程序的总时间进行排序,因此你
可以先打开树的深度,分析对性能影响最大的部分。
呼叫树过滤器
如果所有类的方法都显示在调用树中,那么这个树通常太深了,无法管理。如果你的应用程
序被一个框架调用,那么调用树的顶端将由你不关心的框架类组成,而你自己的类将被深深埋没。
对库的调用将显示其内部结构,可能有上百层的方法调用,你不熟悉也无法影响。
解决这个问题的方法是对调用树进行过滤,这样只有一些类被记录。作为一个积极的副作用,
需要收集的数据更少,需要检测的类也更少,所以开销也就减少了。
默认情况下,剖析会话被配置为一个排除常用框架和库的软件包列表。
当然这个列表是不完整的,所以你最好删除它并自己定义感兴趣的包。事实上,仪器化和默认过
滤器的组合是非常不可取的,JProfiler建议在会话启动对话框中进行更改。
过滤器的表达式与完全合格的类名进行比较,因此com.mycorp.与所有嵌套包中的类相匹配,如
com.mycorp.myapp.Application。 有 三 种 类 型 的 过 滤 器 , 称 为 "profiled"、 "compact "和
"
ignored"。在 "profiled "类中的所有方法都被测量。这是你自己的代码所需要的。
在一个被 "紧凑 "过滤器包含的类中,只有对该类的第一次调用被测量,但没有进一步的内部调
用被显示。"紧凑 "是你对库的要求,包括JRE。例如,当调用hashMap.put(a, b)时,你可能希
望在调用树中看到HashMap.put(),但不能超过这个范围--除非你是map实现的开发者,否则
其内部工作应该被视为不透明的。
最后,"被忽略 "的方法根本不被分析。由于开销的考虑,它们可能不适合被检测,或者它们可能
只是在调用树中分散注意力,比如在动态调用之间插入的Groovy内部方法。
手动输入包是容易出错的,所以你可以使用包浏览器。在你开始会话之前,包浏览器只能显示
配置的类路径中的包,这往往不包括所有实际加载的类。在运行时,包浏览器会向你显示所有
加载的类。
配置的过滤器列表从上到下对每个类别进行评估。在每个阶段,如果有匹配的过滤器,当
前的过滤器类型可能会改变。什么类型的过滤器在过滤器列表中开始是很重要的。如果你从
"
profiled "过滤器开始,一个类的初始过滤器类型是 "compact",意味着只有明确的匹配才会被
profiled。
a.A a.b.B a.b.c.C d.D
默认情
况下:
1
2
3
a.*
a.b.*
紧凑型匹
配
A.B.C.
*
结果:
如果你用一个 "紧凑型 "过滤器启动它,一个类的初始过滤器类型是 "profiled"。在这种情况下,
除了明确排除的类之外,所有的类都被剖析了。
a.A a.b.B a.b.c.C d.D
默认情
况下:
1
2
3
a.*
a.b.*
A.B.C.
*
紧凑型匹
配
结果:
呼叫树时间
为了正确解释调用树,理解调用树节点上显示的数字很重要。对于任何节点来说,有两个时间是有
趣的,即总时间和自我时间。自身时间是该节点的总时间减去嵌套节点的总时间。
通常情况下,自我时间很小,除了紧凑过滤的类。大多数情况下,一个紧凑过滤的类是一个叶子节
点,总时间等于自我时间,因为没有子节点。有时,紧凑过滤类会调用一个剖析类,例如通过回调
或因为它是调用树的入口点,像当前线程的运行方法。在这种情况下,一些未剖析的方法已
经消耗了时间,但没有显示在调用树中。这些时间会涌向调用树中第一个可用的祖先节点
,
并对经过压缩过滤的类的自我时间作出贡献。
实际呼叫序列
过滤后的呼叫序列
A: 自身时间 1
ms
B: 自身时间2ms
X: 自身时间 3
B: 自身时间6毫秒
ms
Y: 自身时间 1
ms
紧凑的外
形设计
C: 自身时间 3
ms
调用树中的百分比条显示总时间,但自我时间部分用不同颜色显示。除非同一层次的两个方法
是重载的,否则方法的显示是没有签名的。有多种方法可以自定义调用树节点的显示,包括
视图设置对话框。例如,你可能想以文本形式显示自我时间或平均时间,总是显示方法签名或
改变使用的时间尺度。另外,百分比的计算可以基于父级时间而不是整个调用树的时间。
线路状态
在调用树的顶部有几个视图参数,可以改变显示的剖析数据的类型和范围。默认情况下,所有
线程都是累积的。JProfiler以每线程为基础维护CPU数据,你可以显示单个线程或线程组。
在任何时候,每个线程都有一个相关的线程状态。如果线程已经准备好处理字节码指令,或者目前
正在CPU核上执行这些指令,那么线程状态被称为 "可运行"。在寻找性能瓶颈时,这种线程状
态是有意义的,所以它被默认选择。
另外,一个线程可以在监视器上等待,例如通过调用Object.wait()或Thread.sleep(),在
这种情况下,线程状态被称为 "等待"。一个线程在试图获取一个监视器时被阻塞,例如在异步
代码块的边界处被阻塞,则处于 "阻塞 "状态。
最后,JProfiler增加了一个合成的 "Net I/O "状态,跟踪线程等待网络数据的时间。这对于分析
服务器和数据库驱动很重要,因为这个时间可能与性能分析有关,比如调查缓慢的SQL查询。
如果你对壁钟时间感兴趣,你必须选择线程状态 "所有状态",同时选择一个单线程。只有这样
,
。
你才能将时间与你在代码中调用System.currentTimeMillis()计算的持续时间进行比较
如果你想把选定的方法转移到不同的线程状态,你可以通过方法触发器和 "覆盖线程状态 "触发
器动作,或者通过使用嵌入式[第140页]或注入式[第135页]探针API中的ThreadStatus类来实
现。
寻找调用树中的节点
有两种方法可以在调用树中搜索文本。首先是快速搜索选项,通过从菜单中调用 "
->
"
或直接开始在调用树中输入来激活。在按下PageDown键后,匹配的内容将被高亮显示,搜索选
项也可用。通过ArrowUp和ArrowDown键,你可以循环浏览不同的匹配信息。
另一种搜索方法、类或包的方式是使用调用树底部的视图过滤器。在这里你可以输入一个用逗号
分隔的过滤器表达式列表。以"-"开头的过滤器表达式就像紧凑型过滤器,否则就像剖析型过滤器
。
就像过滤器的设置一样,最初的过滤器类型决定了默认情况下是包括还是排除类。
点击视图设置文本字段左边的图标可以显示视图过滤器选项。默认情况下,匹配模式是 "包
含",但在搜索特定软件包时,"以 "开始可能更合适。
对呼叫树的不同看法
虽然所有的测量都是针对方法进行的,但JProfiler允许你通过聚合类或包级别上的调用树来获得
更广泛的视角。聚合级别选择器还包含一个 "Java EE组件 "模式。如果你的应用程序使用了
Java EE,那么当调用堆栈跨越Java EE组件的边界时,调用树将显示分割调用树的额外节点。
Java EE组件 "聚合级别删除了所有方法节点,只在树中留下组件节点。
另一种查看调用树的方式是树状图。树状图中的每个矩形代表树中的一个特定节点。矩形的面积与
树状视图中大小条的长度成正比。与树相比,树状图给你的是树中所有叶子的扁平化视角。如果你
对树的主要叶子感兴趣,你可以使用树状图,以便迅速找到它们,而不必深入到树的分支
中。另外,树状图还能让你对叶子节点的相对重要性有一个总体印象。
根据设计,树状图只显示叶子结点的值。分支节点仅以叶子节点的嵌套方式来表达。对于具有显
著自我价值的非叶子节点,JProfiler构建了合成子节点。在下图中,你可以看到节点A的自值为
2
0%,这样它的子节点的总和为80%。为了在树状图中显示A的20%的自值,我们创建了一个总价
值为20%的合成子节点A'。它是一个叶子节点,是B1和B2的兄弟姐妹节点。A'在树状图中会显
示为一个彩色的矩形,而A仅用于确定其子节点B1、B2和A'的几何排列。
A
总数: 100%
内在的:20%
B1
总数:40%
B2
总数:40%
A'
总数: 20%
固有的:0
固有的:0
内在的:20%
树状图节点的实际信息显示在工具提示中,当你将鼠标悬停在树状图上时,会立即显示。这些数
字对应于在树状视图模式下显示的信息。树状图显示的最大嵌套深度为25层,其比例总是相对于
当前显示的节点而言。
更高的聚合层以及树状图都是一种从方法层的细节中抽身而出的方式,并采取鸟瞰的方式。然
而,当你找到一个特别感兴趣的点时,你经常想回到方法层。如果一个节点被选中,而你改变
了方法的聚合级别,JProfiler会尽可能地保留调用栈。通过树状图,上下文菜单中的 "
"操作提供了一种回到调用树中的方法。
热点
如果你的应用程序运行得太慢,你想找到那些花费大部分时间的方法。通过调用树,有时可以
直接找到这些方法,但通常这并不奏效,因为调用树可能是宽泛的,有大量的叶节点。
在这种情况下,你需要调用树的逆向:所有方法的列表,按其总的自我时间排序,由所有不同
的调用栈累积而成,并带有显示这些方法如何被调用的回溯痕迹。在一个热点树中,叶子是入
口点,比如应用程序的主方法或线程的运行方法。从热点树中最深的节点开始,调用会向上传
播到顶层节点。
回溯中的调用次数和执行时间并不是指方法节点,而是指顶层热点节点沿此路径被调用的次数
。
,
这一点很重要,需要理解:粗略一看,你会认为节点上的信息是对该节点的量化调用。然而
在一个热点树中,这些信息显示的是该节点对顶层节点的贡献。因此,你必须像这样阅读这些数
字:沿着这个倒置的调用栈,顶层的热点被调用了n次,总时间为t秒。
呼叫树
热点
方法A
方法C
第5项
第4项
方法C
方法A
计3
计3
回溯
方法B
计2
方法B
计1
热点援用
数
方法C
计1
默认情况下,热点是根据自身时间计算的。你也可以从总时间来计算它们。这对于分析性能瓶
颈不是很有用,但如果你想看到所有方法的列表,那就很有意思了。热点视图只显示最大数量
的方法以减少开销,所以你要找的方法可能根本就不显示。在这种情况下,使用底部的视图过
滤器来过滤包或类。与调用树相反,热点视图过滤器只过滤顶层的节点。热点视图中的截止点不是
全局性的,而是与所显示的类有关,所以在应用过滤器后可能会出现新的节点。
热点和过滤器
热点的概念并不是绝对的,而是取决于调用树过滤器。如果你根本没有调用树过滤器,最大的
热点很可能总是JRE核心类中的方法,如字符串操作、I/O例程或集合操作。这样的热点不会很
有用,因为你往往不能直接控制这些方法的调用,也没有办法加快它们的速度。
为了对你有用,一个热点必须是你自己的类中的一个方法或者是你直接调用的库类中的一个方
法。在调用树过滤器方面,你自己的类在 "profiled "过滤器中,而库类在 "compact "过滤器中。
在解决性能问题时,你可能想消除库层,只看你自己的类。你可以通过在弹出的热点选项中选择
单选按钮,在调用树中快速切换到这个角度。
呼叫图
在调用树以及热点视图中,每个节点都可以出现多次,特别是当调用是递归的时候。在某些情
况下,你对以方法为中心的统计感兴趣,每个方法只出现一次,所有传入和传出的调用都可以
看到。这样的视图最好以图表形式显示,在JProfiler中,它被称为调用图。
图形的一个缺点是它的视觉密度比树要低。这就是为什么JProfiler默认会缩写包名,并且默认会隐
藏总时间少于1%的外发调用。只要节点有出站扩展图标,你就可以再次点击它来显示所有的调
用。在视图设置中,你可以配置这个阈值并关闭包的缩写。
当扩展调用图时,它可能很快变得混乱,特别是当你多次回溯时。使用撤销功能来恢复图的先前状
态。就像调用树一样,调用图提供快速搜索。通过在图中键入,你可以开始搜索。
图形和树状视图各有优缺点,所以有时你可能希望从一种视图类型切换到另一种。在互动会议
中,呼叫树和热点视图显示实时数据并定期更新。然而,呼叫图是按要求计算的,当你扩展节
点时不会改变。呼叫树中的 "
。
"动作会计算一个新的呼叫图,并显示所选的方法
从图表切换到呼叫树是不可能的,因为数据在以后的时间里通常不再具有可比性。然而,呼叫图
提供了呼叫树分析,它的
。
->
动作可以向你显示每个选定节点的累计出站呼叫和回溯的树
超越基础知识
呼叫树、热点视图和呼叫图的组合有许多高级功能,在另一章[第144页]有详细解释。此外,还
有其他先进的CPU视图,将单独介绍[第167页]。
内存 剖析
有两种方法可以获得关于堆上对象的信息。一方面,剖析代理可以跟踪每个对象的分配和垃圾收
集。在JProfiler中,这被称为 "分配记录"。它告诉你对象在哪里被分配了,也可以用来创建关于
临时对象的统计数据。另一方面,JVM的剖析接口允许剖析代理采取 "堆快照",以检查所有活
的对象和它们的引用。要了解对象为什么不能被垃圾回收,就需要这些信息。
分 配 记 录 和 堆 快 照 都 是 昂 贵 的 操 作 。 分 配 记 录 对 运 行 时 特 性 有 很 大 的 影 响 , 因 为
java.lang.Object构造函数必须被仪器化,而且垃圾收集器必须不断向剖析接口报告。这就
是为什么分配在默认情况下不被记录,你必须明确地开始和停止记录[第22页]。拍摄堆快照是一
个一次性的操作。然而,它可能会让JVM暂停几秒钟,而且对获得的数据进行分析可能需要相
对较长的时间,这与堆的大小成正比。
JProfiler将其内存分析分成两个视图部分:实时内存 "部分显示可以定期更新的数据,而 "堆漫
步器 "部分显示静态的堆快照。分配记录是在 "实时内存 "部分控制的,但是记录的数据也会由
堆漫步器显示。
通过内存剖析可以解决的三个最常见的问题是:找到内存泄漏[第185页],减少内存消耗和减少
临时对象的创建。对于前两个问题,你将主要使用堆漫步器,主要是通过查看谁在JVM中持
有最大的对象以及它们是在哪里被创建的。对于最后一个问题,你只能依靠显示记录分配的实
时视图,因为它涉及已经被垃圾回收的对象。
跟踪实例计数
要想了解堆上有哪些对象,"所有对象 "视图会显示所有类和它们的实例计数的柱状图。这个视
图中显示的数据不是通过分配记录来收集的,而是通过执行一个小型的堆快照,只计算实例计
数。堆越大,执行这个操作的时间就越长,所以视图的更新频率会根据测量的开销自动降低。当
视图不活动时,不收集数据,视图不产生任何开销。与大多数动态更新的视图一样,可以使用
工具条按钮来停止更新显示的数据。
另一方面,"记录的对象 "视图只显示在你开始分配记录后被分配的对象的实例计数。当你停止
分配记录时,没有新的分配被添加,但垃圾回收继续被跟踪。通过这种方式,你可以看到在某
个用例中,堆上还有哪些对象。注意,对象可能在很长一段时间内不会被垃圾收集。通过
GC工具条上的按钮,你可以加快这个过程。
当寻找内存泄漏时,你经常想比较一段时间内的实例数。要做到这一点,对于所有的类,你可以使
用视图的差分功能。使用标记当前的工具栏按钮,会插入一个差值列,实例计数的直方图以绿色显
示标记时的基线值。
对于选定的班级,你也可以通过上下文菜单中的
解决的图表。
操作来显示一个时间
分配点
当分配记录被激活时,JProfiler注意到每次分配对象时的调用堆栈。它不使用确切的调用堆栈,例如
来自堆栈行走API,因为这将是非常昂贵的。相反,它使用的是为CPU剖析配置的相同机制。这意味
着调用堆栈是根据调用树过滤器[第45页]来过滤的,实际分配点可能在调用堆栈中不存在的方法中,
因为它来自一个被忽略的或紧凑过滤的类。然而,这些变化在直觉上是很容易理解的:一个经过压
缩过滤的方法要对所有的分配负责,这些分配是在进一步调用压缩过滤的类时进行的。
如果你使用抽样,分配点就会变得近似,可能会让人困惑。与时间测量不同的是,你通常对某
些类的分配点有一个明确的概念,并且
哪些地方没有。由于抽样所描绘的是一个统计而非精确的画面,你可能会看到一些看似不可能的分
配点,比如java.util.HashMap.get分配了你自己的一个类。对于任何需要精确数字和调用堆栈
的分析,建议使用分配记录和仪器分析。
就像CPU剖析一样,分配调用栈以调用树的形式呈现,只是有分配计数和分配的内存,而不是调用
计数和时间。与CPU调用树不同的是,分配调用树不是自动显示和更新的,因为计算该树的成
本更高。JProfiler不仅可以显示所有对象的分配树,还可以显示选定类或包的分配树。与其他选
项一起,这是在选项对话框中配置的,在你要求JProfiler从当前数据中计算出分配树后,该对话
框会显示。
CPU调用树的一个有用的属性是,你可以从上到下跟踪累积的时间,因为每个节点都包含在子
节点中花费的时间。默认情况下,分配树的行为方式也是如此,也就是说,每个节点都包含了
子节点进行的分配。即使分配只是由调用树深处的叶子节点执行的,这些数字也会向上传播到
顶部。通过这种方式,在打开分配调用树的分支时,你总是可以看到哪条路径值得调查。"自我
分配 "是指那些实际由一个节点执行的分配,而不是由其后代执行的分配。像在CPU调用树中一
样,百分比条用不同的颜色显示它们。
在分配调用树中,经常有很多节点根本没有进行分配,特别是当你显示一个选定类的分配时。
这些节点只是为了向你展示通向实际分配发生的节点的调用栈。这样的节点在JProfiler中被称为
"桥梁 "节点,并以灰色图标显示,如上图所示
屏幕截图。在某些情况下,分配的累积可能会妨碍到你,你只想看到实际的分配点。分配树的视
图设置提供了一个选项,可以为此目的显示未累积的数字。如果激活,桥梁节点将始终显示零分
配,没有百分比条。
分配热点视图与分配调用树一起填充,允许你直接关注负责创建选定类的方法。与记录的对象
视图一样,分配热点视图支持标记当前状态并观察随时间变化的差异。视图中添加了一个差异
列,显示自调用
动作以来热点的变化程度。因为分配视图默认不定期更新,你必须点
击
工具条上的按钮来获得新的数据集,然后与基线值进行比较。自动更新在选项对话框中
是可用的,但对于大的堆规模不建议使用。
分配记录率
记录每一个分配都会增加大量的开销。在许多情况下,分配的总数字并不重要,相对数字就足以解
决问题。这就是为什么JProfiler默认只记录每10个分配。与记录所有分配相比,这将开销减少到
大约1/10。如果你想记录所有的分配,或者如果即使是
较少的分配足以满足你的目的,你可以在记录的对象视图以及分配调用树和热点视图的参数对话
框中改变记录率。
该设置也可以在会话设置对话框的 "高级设置->内存剖析 "步骤中找到,它可以为离线剖析会话进
行调整。
分配记录率影响 "记录的对象 "和 "记录的吞吐量 "的虚拟机遥测数据,其值将按配置的分数测量
。
。
在比较快照时[第111页],将报告第一个快照的分配率,如果有必要,其他快照将被相应调整
分析被分配的类别
在计算分配树和分配热点视图时,你必须预先指定你想看到的分配的类或包。如果你已经关注
了特定的类,这就很好,但是当你试图在没有任何预先概念的情况下找到分配热点时,就很不
方便了。一种方法是开始查看 "记录的对象 "视图,并使用上下文菜单中的操作来切换到分配树
或选定类或包的分配热点视图。
另一种方法是,从所有类的分配树或分配热点开始,用
行动,显示所选分配点或分配热点的班级。
所分配的类的直方图被显示为呼叫树分析[第162页]。这个动作也可以从其他的调用树分析中进
行。
类分析视图是静态的,在重新计算分配树和热点视图时不会更新。
分配树,然后根据新数据重新计算当前分析视图。
动作将首先更新
分析垃圾回收对象
分配记录不能只显示实时对象,也要保留垃圾回收对象的信息。这在调查临时分配时很有用。
分配大量的临时对象会产生大量的开销,所以降低分配率往往能大大改善性能。
要在记录的对象视图中显示垃圾收集的对象,请将有效性选择器改为
分配调用树和分配热点视图的选项对话框有一个类似的下拉菜单。
或
。
然而,JProfiler默认不收集垃圾收集对象的分配树信息,因为只维护实时对象的数据,其开销要
小得多。当把 "分配调用树 "或 "分配热点 "视图中的有效性选择器切换到包括垃圾收集对象的模
式时,JProfiler建议改变记录类型。这是剖析设置的改变,所以如果你选择立即应用这个改变,
所有以前记录的数据将被清除。如果你想提前改变这个设置,你可以在会话设置对话框的 "高级
设置"->"内存剖析 "中这样做。
下一站:堆栈步行者
任何更高级的问题类型都会涉及对象之间的引用。例如,在记录对象、分配树和分配热点视图
中显示的尺寸是浅层尺寸。它们只包括类的内存布局,但不包括任何引用的类。要想知道一个类的
对象到底有多重,你经常想知道保留的大小,也就是说,如果这些对象从堆中被移除,将被释放的
内存量。
这种信息在实时内存视图中是不可用的,因为它需要列举堆上的所有对象并进行昂贵的计算。这
项工作是由堆漫步器来处理的。要从实时内存视图中的一个兴趣点跳到堆漫步器中,可以使用
工具条上的按钮。它将带你到堆运行器中的相应视图。
如果没有可用的堆快照,将创建一个新的堆快照,否则JProfiler将询问您是否使用现有的堆快照
。
在任何情况下,重要的是要理解实时内存视图中的数字和堆漫步器中的数字往往会有很大的不同
。
除了堆漫步器显示了与实时内存视图不同的时间点的快照之外,它还消除了所有未引用的对象
根据垃圾收集器的状态,未引用的对象可能占据了堆的很大一部分。
。
The Heap Walker
堆积快照
任何涉及对象间引用的堆分析都需要堆快照,因为不可能问JVM对一个对象的传入引用是什么--
你必须遍历整个堆才能回答这个问题。从该堆快照中,JProfiler创建了一个内部数据库,该数据
库为产生服务于堆漫步器中的视图所需的数据而优化。
有两个堆快照的来源:JProfiler堆快照和HPROF/PHD堆快照。JProfiler堆快照支持堆漫步器的所
有可用功能。剖析代理使用剖析接口JVMTI来迭代所有引用。如果被剖析的JVM运行在不同的机器
上,所有的信息将被转移到本地机器上,并在那里进行进一步的计算。HPROF/PHD快照是通
过JVM的内置机制创建的,并以JProfiler可以读取的标准格式写入磁盘。HPROF快照由HotSpot
JVM提供,PHD快照由Eclipse OpenJ9 JVM产生。
在heap walker的概述页面,你可以选择是否应该创建JProfiler heap snapshot或HPROF/PHD
heap snapshot。默认情况下,推荐使用JProfiler堆快照。HPROF/PHD堆快照在特殊情况下很有
用,这将在另一章[第178页]讨论。
选择步骤
堆漫步器由几个视图组成,显示选定的一组对象的不同方面。就在你拍下堆快照之后,你就在看堆
上的所有对象了。每个视图都有导航动作,用于将一些选定的对象变成当前的对象集。堆漫步
器的头部区域显示了当前对象集中包含多少个对象的信息。
68
最初,你看到的是 "类 "视图,它与实时内存部分[第59页]的 "所有对象 "视图相似。通过选择一
个类并调用Use->Selected Instances,你创建了一个只包含该类实例的新对象集。在堆漫步器中,"
使用 "总是意味着创建一个新的对象集。
对于新的对象集,显示堆漫步器的类视图并不有趣,因为它实际上只是将表过滤到先前选择的
类。相反,JProfiler建议用 "新对象集 "对话框来显示另一个视图。你可以取消这个对话框来丢
弃新的对象集并返回到之前的视图。建议使用流出的引用视图,但你也可以选择其他视图。这
只是针对最初显示的视图,之后你可以在堆漫步器的视图选择器中切换视图。
现在标题区告诉你有两个选择步骤,并包括计算保留尺寸和深度尺寸的链接,或者使用当前对象
集保留的所有对象。后者将增加一个选择步骤,并建议使用类视图,因为该对象集可能有多个
类。
在堆漫步器的下部,列出了到此为止的选择步骤。点击超链接将带你回到任何一个选择步骤。
第一个数据集也可以通过工具条上的Go To Start按钮到达。如果你在分析中需要回溯,工具栏中
的后退和前进按钮很有用。
班级查看
堆漫步器顶部的视图选择器包含五个视图,显示当前对象集的不同信息。其中第一个是 "Classes
"
视图。
类视图类似于实时内存部分的 "所有对象 "视图,它有一个聚合级别选择器,可以将类分成包。此
外,它还可以显示类的估计保留大小。这是指如果一个类的所有实例被从堆中移除,将被释放的
内存量。如果你点击
超链接,就会增加一个新的
列。显示的保留
大小是估计的下限,计算确切的数字会太慢。如果你真的需要一个确切的数字,请选择感兴趣
的类或包,并使用新对象集标题中的 的超链接。
基于你对一个或多个类或包的选择,你可以选择实例本身,相关的java.lang.Class对象,或所
有保留的对象。双击是最快速的选择模式,并使用选定的实例。如果有多种选择模式,就像本例一
样,在视图上方会显示一个使用下拉菜单。
在解决与类加载器有关的问题时,你经常要按类加载器来分组实例。
类加载器分组 "的检查,该检查是可用的