JVM监控及诊断工具(GUI篇二)
JProfiler
基本概述
介绍
在运行Java的时候有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在eclipse里面有Eclipse Memory Analyzer tool(MAT)插件可以测试,而在IDEA中也有这么一个插件,就是JProfiler。
JProfiler 是由 ej-technologies 公司开发的一款 Java 应用性能诊断工具。功能强大,但是收费。
官网下载地址:https://www.ej-technologies.com/products/jprofiler/overview.html
特点
- 使用方便、界面操作友好(简单且强大)
- 对被分析的应用影响小(提供模板)
- CPU、Thread、Memory分析功能尤其强大
- 支持对jdbc、NoSQL、jsp、servlet、socket等进行分析
- 支持多种模式(离线,在线)的分析
- 支持监控本地、远程的JVM
- 跨平台,拥有多种操作系统的安装版本
主要功能
-
方法调用
对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法
-
内存分配
通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄漏问题,优化内存使用
-
线程和锁
JProfiler提供多种针对线程和锁的分析视图助您发现多线程问题
-
高级子系统
许多性能问题都发生在更高的语义级别上。例如,对于JDBC调用,您可能希望找出执行最慢的SQL语句。JProfiler支持对这些子系统进行集成分析
安装与配置
JProfiler中配置IDEA
IDEA集成JProfiler
具体使用
数据采集方式
JProfier数据采集方式分为两种:Sampling(样本采集)和Instrumentation(重构模式)
Instrumentation重构模式
这是JProfiler全功能模式。在class加载之前,JProfier把相关功能代码写入到需要分析的class的bytecode中,对正在运行的jvm有一定影响。
- 优点:功能强大。在此设置中,调用堆栈信息是准确的。
- 缺点:若要分析的class较多,则对应用的性能影响较大,CPU开销可能很高(取决于Filter的控制)。因此使用此模式一般配合Filter使用,只对特定的类或包进行分析
Sampling抽样模式
类似于样本统计,每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来。
- 优点:对CPU的开销非常低,对应用影响小(即使你不配置任何Filter)
- 缺点:一些数据/特性不能提供(例如:方法的调用次数、执行时间)
注:JProfiler本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型。因为JProfiler的绝大多数核心功能都依赖方法调用采集的数据,所以可以直接认为是JProfiler的数据采集类型。
遥感监测 Telemetries
查看JVM的运行信息
- 整体视图Overview:显示堆内存、CPU、线程以及GC等活动视图。
- 内存Memory:显示一张关于内存变化的活动时间表。
- 记录的对象Recorded objects:显示一张关于活动对象与数组的图表的活动时间表。
- 记录吞吐量Record Throughput:显示一段时间累计的JVM生产和释放的活动时间表。
- 垃圾回收活动GC Activity:显示一张关于垃圾回收活动的活动时间表。
- 类Classes:显示一个与已装载类的图表的活动时间表。
- 线程Threads:显示一个与动态线程图表的活动时间表。
- CPU负载CPU Load:显示一段时间中CPU的负载图表。
概述:
内存情况:
对象情况:
吞吐量:
GC情况:
类情况:
线程情况:
CPU负载情况:
内存视图 Live Memory
class/class instance的相关信息。例如对象的个数,大小,对象创建的方法执行栈,对象创建的热点。
-
所有对象All Objects:显示所有加载的类的列表和在堆上分配的实例数。只有Java 1.5(JVMTI)才会显示此视图。
-
记录对象Record Objects:查看特定时间段对象的分配,并记录分配的调用堆栈。
-
分配访问树 Allocation Call Tree:显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。
-
分配热点 Allocation Hot Spots:显示一个列表,包括方法、类、包或分配已选类的J2EE组件。你可以标注当前值并且显示差异值。对于每个热点都可以显示它的跟踪记录树。
-
类追踪器 Class Tracker:类跟踪视图可以包含任意数量的图表,显示选定的类和包的实例与时间。
分析:内存中的对象的情况
- 频繁创建的Java对象:死循环、循环次数过多
- 存在大的对象:读取文件时,byte[]应该边读边写。如果长时间不写出的话,导致byte[]过大
- 存在内存泄漏
所有对象:浅堆
点击Mark Current可以直接进行对比
记录对象:判断内存泄漏时使用,默认不开启,当发现不能清理的内存越来越多时需要开启
查看对象
堆遍历 Heap Walker
- 类 Classes:显示所有类和它们的实例,可以右击具体的类“Used Selected Instance”实现进一步跟踪。
- 分配 Allocations:为所有记录对象显示分配树和分配热点。
- 索引 References:为单个对象和“显示到垃圾回收根目录的路径”提供索引图的显示功能。还能提供合并输入视图和出视图的功能。
- 时间 Time:显示一个对已记录对象的解决时间的柱状图。
- 检查 Inspections:显示了一个数量的操作,将分析当前对象集在某种条件下的子集,实质是一个筛选的过程。
- 图表 Graph:你需要在references视图和biggest视图手动添加对象到图表,它可以显示对象的传入和传出引用,能方便的找到垃圾收集器根源。
PS:在工具栏点击“Go To Start”可以使堆内存重新计数,也就是回到初始状态。
右键Picture,选Use Selected Picture
可以选择入引用和出引用
可以看到被一个ArrayList引用
也可以选择使用图表展示
可以生成堆转储文件
CPU视图 CPU Views
JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。
访问树 Call Tree
显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。JDBC、JMS和JNDI服务请求
都被注释在请求树中。请求树可以根据Servlet和JSP对URL的不同需要进行拆分。
热点 Hot Spots
显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC、JMS和JNDI服务请求以及按照URL请求来进行计算。
访问图 Call Graph
显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。
方法统计 Method Statistics
显示一段时间内记录的方法的调用时间细节。
Call Tree:通过方法执行时间来刻画CPU的使用情况
线程视图 Threads
JProfiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析。
线程历史 Thread History
显示一个与线程活动和线程状态在一起的活动时间表。
线程监控 Thread Monitor
显示一个列表,包括所有的活动线程以及它们目前的活动状况。
线程转储 Thread Dumps
显示所有线程的堆栈跟踪。
线程分析主要关心三个方面:
- web容器的线程最大数。比如:Tomcat的线程容量应该略大于最大并发数。
- 线程阻塞
- 线程死锁
监视器&锁 Monitor&Locks
所有线程持有锁的情况以及锁的信息
死锁探测图表 Current Locking Graph
显示JVM中的当前死锁图表
监测器 Current Monitors
显示目前使用的监测器并且包括它们的关联线程
锁定历史图表 Locking History Graph
显示记录在JVM中的锁定历史
案例分析
案例1
package com.atguigu.java;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class JProfilerTest {
public static void main(String[] args) {
while (true) {
ArrayList<Data> list = new ArrayList<>();
int num = 500;
for (int i = 0; i < num; i++) {
Data data = new Data();
list.add(data);
}
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Data {
private int size = 10;
private byte[] buffer = new byte[1024 * 1024];
private String info = "hello,atguigu";
}
比较良性的情况
案例2
package com.atguigu.java;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class MemoryLeak {
public static void main(String[] args) {
while (true) {
ArrayList<Bean> beanlist = new ArrayList<>();
int num = 500;
for (int i = 0; i < num; i++) {
Bean data = new Bean();
data.list.add(new byte[1024 * 10]);
beanlist.add(data);
}
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Bean {
int size = 109;
String info = "cpuCode";
static ArrayList list = new ArrayList();
// ArrayList list = new ArrayList();
}
byte数组一直往上涨
发现是源自于Bean里面的ArrayList
Arthas
基本概述
背景
前面,我们介绍了jdk自带的jvisualvm等免费工具,以及商业化工具Jprofiler。
这两款工具在业界知名度也比较高,他们的优点是可以图形界面上看到各维度的性能数据,使用者根据这些数据进行综合分析,然后判断哪里出现了性能问题。
但是这两款工具也有个缺点,都必须在服务端项目进程中配置相关的监控参数。然后工具通过远程连接到项目进程,获取相关的数据。这样就会带来一些不便,比如线上环境的网络是隔离的,本地的监控工具根本连不上线上环境。并且类似于Jprofiler这样的商业工具,是需要付费的。
那么有没有一款工具不需要远程连接,也不需要配置监控参数,同时也提供了丰富的性能监控数据呢?
今天跟大家介绍一款阿里巴巴开源的性能分析神器Arthas(阿尔萨斯)
概述
Arthas(阿尔萨斯)是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启,动态跟踪]ava代码,实时监控JVM状态。
Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
基于哪些工具开发而来
- greys-anatomy:Arthas代码基于Greys二次开发而来,非常感谢Greys之前所有的工作,以及Greys原作者对Arthas提出的意见和建议!
- termd:Arthas的命令行实现基于termd开发,是一款优秀的命令行程序开发框架,感谢termd提供了优秀的框架。
- crash:Arthas的文本渲染功能基于crash中的文本渲染功能开发,可以从这里看到源码,感谢crash在这方面所做的优秀工作。
- cli:Arthas的命令行界面基于vert.x提供的cli库进行开发,感谢vert.x在这方面做的优秀工作。
- compiler:Arthas里的内存编译器代码来源
- Apache Commons Net Arthas里的Telnet Client代码来源
- JavaAgent:运行在main方法之前的拦截器,它内定的方法名叫premain,也就是说先执行premain方法然后再执行main方法
- ASM:一个通用的Java字节码操作和分析框架。它可以用于修改现有的类或直接以二进制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从它们构建定制的复杂转换和代码分析工具。ASM提供了与其他Java字节码框架类似的功能,但是主要关注性能。因为它被设计和实现得尽可能小和快,所以非常适合在动态系统中使用(当然也可以以静态方式使用,例如在编译器中)
官方使用文档
https://arthas.aliyun.com/zh-cn/
安装与使用
安装
安装方式一:可以直接在Linux上通过命令下载
可以在官方 Github 上进行下载,如果速度较慢,可以尝试国内的码云 Gitee 下载。
github下载
wget https://alibaba.github.io/arthas/arthas-boot.jar
Gitee下载
wget https://arthas.gitee.io/arthas-boot.jar
安装方式二:
也可以在浏览器直接访问https://alibaba.github.io/arthas/arthas-boot.jar,等待下载成功后,上传到Linux服务器上。
卸载:
在Linux/Unix/Mac平台
删除下面文件:
rm-rf~/.arthas/I
rm -rf ~/logs/arthas
Windows平台直接删除user home下面的.arthas和logs/arthas目录
启动
Arthas 只是一个 java 程序,所以可以直接用java -jar
运行。
执行成功后,arthas提供了一种命令行方式的交互方式,arthas会检测当前服务器上的Java进程,并将进程列表展示出来,用户输入对应的编号(1、2、3、4…)进行选择,然后回车。
方式1:
shinomiya@shinomiyadeMBP Documents % java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.4.5
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 545 nutstore.client.gui.NutstoreGUI
[2]: 1428
[3]: 1449 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
[4]: 2509 org.jetbrains.jps.cmdline.Launcher
[5]: 2510 com.atguigu.java.OOMTest
[6]: 479
方式2:运行时选择 Java 进程 PID
java -jar arthas-boot.jar [PID]
查看进程
查看日志
cat ~/logs/arthas/arthas.log
查看帮助
java -jar arthas-boot.jar -h
Web Console
除了在命令行查看外,Arthas
目前还支持Web Console
。在成功启动连接进程之后就已经自动启动,可以直接访问http://127.0.0.1:8563/
访问,页面上的操作模式和控制台完全一样。
相关诊断命令
基础指令
- help:查看命令帮助信息
- cat:打印文件内容,和linux里的cat命令类似
- echo:打印参数,和linux里的echo命令类似
- grep:匹配查找,和linux里的grep命令类似
- base64:base64编码转换,和linux里的base64命令类似
- tee:复制标准输入到标准输出和指定的文件,和linux里的tee命令类似
- pwd:返回当前的工作目录,和linux命令类似
- cls:清空当前屏幕区域
- session:查看当前会话的信息
- reset:重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
- version:输出当前目标 Java 进程所加载的 Arthas 版本号
- history:打印命令历史
- quit:退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
- stop:关闭 Arthas 服务端,所有 Arthas 客户端全部退出
- keymap:Arthas快捷键列表及自定义快捷键
JVM相关
- dashboard:当前系统的实时数据面板
- thread:查看当前 JVM 的线程堆栈信息
- jvm:查看当前 JVM 的信息
- sysprop:查看和修改JVM的系统属性
- sysenv:查看JVM的环境变量
- vmoption:查看和修改JVM里诊断相关的option
- perfcounter:查看当前 JVM 的Perf Counter信息
- logger:查看和修改logger
- getstatic:查看类的静态属性
- ognl:执行ognl表达式
- mbean:查看 Mbean 的信息
- heapdump:dump java heap, 类似jmap命令的heap dump功能
dashboard
当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps, rt, 错误数, 线程池信息等等。
参数名称 | 参数说明 |
---|---|
[i:] | 刷新实时数据的时间间隔 (ms),默认5000ms |
[n:] | 刷新实时数据的次数 |
thread
查看当前线程信息,查看线程的堆栈
参数名称 | 参数说明 |
---|---|
id | 线程id |
[n:] | 指定最忙的前N个线程并打印堆栈 |
[b] | 找出当前阻塞其他线程的线程 |
[i <value> ] |
指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200 |
[--all] | 显示所有匹配的线程 |
jvm
class/classloader相关
mc、redefind
Memory Compiler/内存编译器,编译.java
文件生成.class
推荐使用 retransform 命令
加载外部的.class
文件,redefine jvm已加载的类。
参数名称 | 参数说明 |
---|---|
[c:] | ClassLoader的hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
sc
查看JVM已加载的类信息
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
[d] | 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。 如果一个类被多个ClassLoader所加载,则会出现多次 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[f] | 输出当前类的成员变量信息(需要配合参数-d一起使用) |
[x:] | 指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出 |
[c:] |
指定class的 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[n:] |
具有详细信息的匹配类的最大数量(默认为100) |
补充:
-
class-pattern支持全限定名,如com.taobao.test.AAA,也支持com/taobao/test/AAA这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把
/
替换为.
啦。 -
sc 默认开启了子类匹配功能,也就是说所有当前类的子类也会被搜索出来,想要精确的匹配,请打开
options disable-sub-class true
开关
sm
查看已加载类的方法信息
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
[d] | 展示每个方法的详细信息 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[c:] |
指定class的 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[n:] |
具有详细信息的匹配类的最大数量(默认为100) |
jad
反编译指定已加载类的源码
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
[c:] |
类所属 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
Classloader
查看classloader的继承树,urls,类加载信息
参数名称 | 参数说明 |
---|---|
[l] | 按类加载实例进行统计 |
[t] | 打印所有ClassLoader的继承树 |
[a] | 列出所有ClassLoader加载的类,请谨慎使用 |
[c:] |
ClassLoader的hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[c: r:] |
用ClassLoader去查找resource |
[c: load:] |
用ClassLoader去加载指定的类 |
monitor/watch/trace相关
请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 stop 或将增强过的类执行 reset 命令。
- monitor:方法执行监控
- watch:方法执行数据观测
- trace:方法内部调用路径,并输出方法路径上的每个节点上耗时
- stack:输出当前方法被调用的调用路径
- tt:方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
stack
输出当前方法被调用的调用路径
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[n:] |
执行次数限制 |
monitor
方法执行监控
参数说明
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[c:] |
统计周期,默认值为120秒 |
[b] | 在方法调用之前计算condition-express |
监控的维度说明
监控项 | 说明 |
---|---|
timestamp | 时间戳 |
class | Java类 |
method | 方法(构造方法、普通方法) |
total | 调用次数 |
success | 成功次数 |
fail | 失败次数 |
rt | 平均RT |
fail-rate | 失败率 |
trace
方法内部调用路径,并输出方法路径上的每个节点上耗时
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[n:] |
命令执行次数 |
#cost |
方法执行耗时 |
watch
方法执行数据观测
让你能方便的观察到指定方法的调用情况。能观察到的范围为:返回值
、抛出异常
、入参
,通过编写 OGNL 表达式进行对应变量的查看。
参数说明
watch 的参数比较多,主要是因为它能在 4 个不同的场景观察对象
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
express | 观察表达式 |
condition-express | 条件表达式 |
[b] | 在方法调用之前观察 |
[e] | 在方法异常之后观察 |
[s] | 在方法返回之后观察 |
[f] | 在方法结束之后(正常返回和异常返回)观察 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[x:] | 指定输出结果的属性遍历深度,默认为 1 |
说明
这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写"{params,returnObj}"
,只要是一个合法的 ognl 表达式,都能被正常支持。
观察的维度也比较多,主要体现在参数 advice
的数据结构上。Advice
参数最主要是封装了通知节点的所有信息。请参考表达式核心变量中关于该节点的描述。
tt
方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
其他
使用>将结果重写到日志文件,使用&指令命令是后台运行,session断开不影响任务执行生命周期默认为1天)
jobs:列出所有job
kill:强制终止任务
fg:将暂停的任务拉到前台执行
bg:将暂停的任务放到后台执行
grep:搜索满足条件的结果
plaintext:将命令的结果去除ANSI颜色
wc:按行统计输出结果
options:查看或设置Arthas全局开关
profiler:使用async-profiler对应用采样,生成火焰图
profiler/火焰图
使用async-profiler生成火焰图
参数名称 | 参数说明 |
---|---|
action | 要执行的操作 |
actionArg | 属性名模式 |
[i:] | 采样间隔(单位:ns)(默认值:10'000'000,即10 ms) |
[f:] | 将输出转储到指定路径 |
[d:] | 运行评测指定秒 |
[e:] | 要跟踪哪个事件(cpu, alloc, lock, cache-misses等),默认是cpu |
options
全局开关
名称 | 默认值 | 描述 |
---|---|---|
unsafe | false | 是否支持对系统级别的类进行增强,打开该开关可能导致把JVM搞挂,请慎重选择! |
dump | false | 是否支持被增强了的类dump到外部文件中,如果打开开关,class文件会被dump到/${application working dir}/arthas-class-dump/ 目录下,具体位置详见控制台输出 |
batch-re-transform | true | 是否支持批量对匹配到的类执行retransform操作 |
json-format | false | 是否支持json化的输出 |
disable-sub-class | false | 是否禁用子类匹配,默认在匹配目标类的时候会默认匹配到其子类,如果想精确匹配,可以关闭此开关 |
support-default-method | true | 是否支持匹配到default method,默认会查找interface,匹配里面的default method。参考 #1105 |
save-result | false | 是否打开执行结果存日志功能,打开之后所有命令的运行结果都将保存到~/logs/arthas-cache/result.log 中 |
job-timeout | 1d | 异步后台任务的默认超时时间,超过这个时间,任务自动停止;比如设置 1d, 2h, 3m, 25s,分别代表天、小时、分、秒 |
print-parent-fields | true | 是否打印在parent class里的filed |
Java Mission Control
历史
在Oracle收购Sun之前,Oracle的JRockit虚拟机提供了一款叫做JRockit Mission Control 的虚拟机诊断工具。
在Oracle收购Sun之后,Oracle公司同时拥有了Sun Hotspot和JRockit两款虚拟机。根据Oracle对于Java的战略,在今后的发展中,会将JRockit的优秀特性移植到Hotspot上。其中,一个重要的改进就是在Sun的JDK中加入了JRockit的支持。
在Oracle JDK 7u40之后,Mission Control这款工具已经绑定在Oracle JDK中发布。
自Java11开始,本节介绍的JFR已经开源。但在之前的Java版本,JFR属于Commercial Feature,需要通过 Java 虚拟机参数-XX:+UnlockCommercialFeatures开启
如果你有兴趣请可以查看OpenJDK的Mission Control项目:https://github.com/JDKMissionControl/jmc
启动
概述
Java Mission Control
(简称JMC),Java官方提供的性能强劲的工具。是一个用于对 Java应用程序进行管理、监视、概要分析和故障排除的工具套件。
它包含一个GUI客户端,以及众多用来收集Java虚拟机性能数据的插件,如JMX Console
(能够访问用来存放虚拟机各个子系统运行数据的MXBeans),以及虚拟机内置的高效profiling
工具Java Flight Recorder
(JFR)。
JMC 的另一个优点就是:采用取样,而不是传统的代码植入技术,对应用性能的影响非常非常小 , 完全可以开着JMC来做压测(唯一影响可能是fullgc多了)。
功能:实时监控JVM运行时的状态
如果是远程服务器,使用前要开JMX。
- Dcom.sun.management.jmxremote.port = $
- Dcom.sun.management.jmxremote
- Dcom.sun.management.jmxremote.authenticate=false
- Dcom.sun.management.jmxremote.ssl=false
- Djava.rmi.server.hostname=$
文件 -> 连接 -> 创建新连接,填入上面JMX参数的host和port
Java Flight Recorder
Java Flight Recorder 是 JMC 的其中一个组件。
Java Flight Recorder能够以极低的性能开销收集 Java 虚拟机的性能数据。
JFR的性能开销很小,在默认配置下平均低于1%。与其他工具相比,JFR能够直接访问虚拟机内的数据,并且不会影响虚拟机的优化。因此,它非常适用于生产环境下满负荷运行的Java程序。
Java Flight Recorder和JDK Mission Control共同创建了一个完整的工具链。JDK Mission Control可对Java Flight Recorder连续收集低水平和详细的运行时信息进行高效 , 详细的分析。
事件类型
当启用时,JFR将记录运行过程中发生的一系列事件。其中包括Java层面的事件,如线程事件、锁事件,以及Java虚拟机内部的事件,如新建对象、垃圾回收和即时编译事件。
按照发生时机以及持续时间来划分,JFR的事件共有四种类型,它们分别为以下四种。
- 瞬时事件(Instant Event),用户关心的是它们发生与否,例如异常、线程启动事件。
- 持续事件(Duration Event),用户关心的是它们的持续时间,例如垃圾回收事件。
- 计时事件(Timed Event),是时长超出指定阙值的持续事件。
- 取样事件(Sample Event)是周期性取样的事件。
取样事件的其中一个常见例子便是方法抽样(Method Sampling),即每隔一段时间统计各个线程的栈轨迹。如果在这些抽样取得的栈轨迹中存在一个反复出现的方法,那么我们可以推测该方法是热点方法。
启动方式
方式1:使用-XX:StartFlightRecording=
参数
第一种是在运行目标 Java 程序时添加-XX:StartFlightRecording=
参数。
比如:下面命令中,JFR将会在Java虚拟机启动5s后(对应delay=5s)收集数据,持续 20s(对应duration=20s)。当收集完毕后,JFR会将收集得到的数据保存至指定的文件中(对应 filename=myrecording.jfr)
java -XX:StartFlightRecording=delay=5s,duration=20s,filename=myrecording.jfr,settings=profile MyApp
由于JFR将持续收集数据,如果不加以限制,那么JFR可能会填满硬盘的所有空间。因此,我们有必要对这种模式下所收集的数据进行限制。
比如:
java -XX:StartFlightRecording=maxage=10m,maxsize=100m,name=SomeLabel MyApp
方式2:使用jcmd的JFR
子命令
通过jcmd来让JFR开始收集数据、停止收集数据,或者保存所收集的数据,对应的子命令分别为JFR.start
、JFR.stop
以及JFR.dump
。
$ jcmd <PID> JFR.start settings=profile maxage=10m maxsize=150m name=SomeLabel
上述命令运行过后,目标进程中的JFR已经开始收集数据。此时,我们可以通过下述命令来导出己经收集到的数据:
$ jcmd <PID> JFR. dump name=SomeLabel filename=myrecording.jfr
最后,我们可以通过下述命令关闭目标进程中的JFR :
$ jcmd <PID> JFR.stop name=SomeLabel
方式3:JMC的JFR插件
Java Flight Recorder 取样分析
要采用取样,必须先添加参数:
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder
取样时间默认1分钟,可自行按需调整,事件设置选为profiling,然后可以设置取样 profile哪些信息,比如:
- 加上对象数量的统计:Java Virtual Machine->GC->Detailed->Object Count/Object Count after GC
- 方法调用采样的间隔从10ms改为1ms(但不能低于1ms,否则会影响性能了):Java Virtual Machine -> Profiling - > Method Profiling Sample/Method Sampling Information
- Socket与File采样,10ms太久,但即使改为1ms也未必能抓住什么,可以干脆取消掉:Java Application -> File Read/Filewrite/Socket Read/Socket Write
然后就开始Profile , 到时间后Profile结束,会自动把记录下载回来,在JMC中展示。
从展示信息中,我们大致可以读到内存和CPU信息、代码、线程和IO等比较重要的信息展示。其他工具
其他
Flame Graph(火焰图)
在追求极致性能的场景下,了解你的程序运行过程中cpu在干什么很重要,火焰图就是一种非常直观的展示cpu在程序整个生命周期过程中时间分配的工具。
火焰图对于现代的程序员不应该陌生,这个工具可以非常直观的显示出调用栈中的CPU消耗瓶颈。
网上的关于java火焰图的讲解大部分来自Brendan Gregg的博客:www.brendangregg.com/flamegraphs…
Tprofiler
案例:
使用JDK自身提供的工具进行JVM调优可以将TPS由2.5提升到20(提升了7倍), 并准确定位系统瓶颈。
系统瓶颈有:应用里静态对象不是太多、有大量的业务线程在频繁创建一些生命周期很长的临时对象 , 代码里有问题。
那么,如何在海量业务代码里边准确定位这些性能代码?这里使用阿里开源工具TProfiler来定位这些性能代码,成功解决掉了GC过于频繁的性能瓶颈,并最终在上次优化的基础上将TPS再提升了4倍,即提升到100。
- TProfiler配置部署、远程操作、日志阅读都不太复杂,操作还是很简单的。但是其却是能够起到一针见血、立竿见影的效果,帮我们解决了GC过于频繁的性能瓶颈。
- TProfiler最重要的特性就是能够统计出你指定时间段内JVM的top method,这些top method 极有可能就是造成你JVM性能瓶颈的元凶。这是其他大多数JVM调优工具所不具备的,包括JRockit Mission Control。JRokit首席开发者Marcus Hirt在其私人博客Low Overhead Method Profiling with Java Mission Control下的评论中曾明确指出 JRMC并不支持TOP方法的统计。
- TProfiler的下载:github.com/alibaba/TPr…
Btrace
Java运行时追踪工具
常见的动态追踪工具有BTrace、HouseMD(该项目已经停止开发)、Greys-Anatomy(国人开发,个人开发者)、Byteman(JBoss出品),注意Java运行时追踪工具并不限于这几种,但是这几个是相对比较常用的。
BTrace是SUN Kenai云计算开发平台下的一个开源项目,旨在为java提供安全可靠的动态跟踪分析工具。先看一下BTrace的官方定义:
BTrace is a safe, dynamic tracing tool for the Java platform. BTrace can be used to dynamically trace a running Java program (similar to DTrace for OpenSolaris applications and OS). BTrace dynamically instruments the classes of the target application to inject tracing code ("bytecode tracing")。
简洁明了,大意是一个Java平台的安全的动态追踪工具。可以用来动态地追踪一个运行的Java程序。
BTrace动态调整目标应用程序的类以注入跟踪代码(“字节码跟踪”)。
另有YourKit、JProbe、Spring Insight等……