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
  • 跨平台,拥有多种操作系统的安装版本

主要功能

  1. 方法调用

    对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法

  2. 内存分配

    通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄漏问题,优化内存使用

  3. 线程和锁

    JProfiler提供多种针对线程和锁的分析视图助您发现多线程问题

  4. 高级子系统

    许多性能问题都发生在更高的语义级别上。例如,对于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

显示所有线程的堆栈跟踪。

线程分析主要关心三个方面:

  1. web容器的线程最大数。比如:Tomcat的线程容量应该略大于最大并发数。
  2. 线程阻塞
  3. 线程死锁

监视器&锁 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(阿尔萨斯)

概述

_images/arthas.png

Arthas(阿尔萨斯)是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启,动态跟踪]ava代码,实时监控JVM状态。

Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?

基于哪些工具开发而来

  • 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

http://localhost:8563

除了在命令行查看外,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

https://arthas.gitee.io/dashboard.html

当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps, rt, 错误数, 线程池信息等等。

参数名称 参数说明
[i:] 刷新实时数据的时间间隔 (ms),默认5000ms
[n:] 刷新实时数据的次数

thread

https://arthas.gitee.io/thread.html

查看当前线程信息,查看线程的堆栈

参数名称 参数说明
id 线程id
[n:] 指定最忙的前N个线程并打印堆栈
[b] 找出当前阻塞其他线程的线程
[i <value>] 指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200
[--all] 显示所有匹配的线程

jvm

https://arthas.gitee.io/jvm.html

class/classloader相关

mc、redefind

https://arthas.gitee.io/mc.html

Memory Compiler/内存编译器,编译.java文件生成.class

https://arthas.gitee.io/redefine.html

推荐使用 retransform 命令

加载外部的.class文件,redefine jvm已加载的类。

参数名称 参数说明
[c:] ClassLoader的hashcode
[classLoaderClass:] 指定执行表达式的 ClassLoader 的 class name
sc

https://arthas.gitee.io/sc.html

查看JVM已加载的类信息

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
[d] 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。 如果一个类被多个ClassLoader所加载,则会出现多次
[E] 开启正则表达式匹配,默认为通配符匹配
[f] 输出当前类的成员变量信息(需要配合参数-d一起使用)
[x:] 指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出
[c:] 指定class的 ClassLoader 的 hashcode
[classLoaderClass:] 指定执行表达式的 ClassLoader 的 class name
[n:] 具有详细信息的匹配类的最大数量(默认为100)

补充:

  1. class-pattern支持全限定名,如com.taobao.test.AAA,也支持com/taobao/test/AAA这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把/替换为.啦。

  2. sc 默认开启了子类匹配功能,也就是说所有当前类的子类也会被搜索出来,想要精确的匹配,请打开options disable-sub-class true开关

sm

https://arthas.gitee.io/sm.html

查看已加载类的方法信息

参数名称 参数说明
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

https://arthas.gitee.io/classloader.html

查看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

https://arthas.gitee.io/monitor.html

方法执行监控

参数说明

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[c:] 统计周期,默认值为120秒
[b] 方法调用之前计算condition-express

监控的维度说明

监控项 说明
timestamp 时间戳
class Java类
method 方法(构造方法、普通方法)
total 调用次数
success 成功次数
fail 失败次数
rt 平均RT
fail-rate 失败率

trace

https://arthas.gitee.io/trace.html

方法内部调用路径,并输出方法路径上的每个节点上耗时

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[n:] 命令执行次数
#cost 方法执行耗时

watch

https://arthas.gitee.io/watch.html

方法执行数据观测

让你能方便的观察到指定方法的调用情况。能观察到的范围为:返回值抛出异常入参,通过编写 OGNL 表达式进行对应变量的查看。

参数说明

watch 的参数比较多,主要是因为它能在 4 个不同的场景观察对象

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式
condition-express 条件表达式
[b] 方法调用之前观察
[e] 方法异常之后观察
[s] 方法返回之后观察
[f] 方法结束之后(正常返回和异常返回)观察
[E] 开启正则表达式匹配,默认为通配符匹配
[x:] 指定输出结果的属性遍历深度,默认为 1

说明

这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写"{params,returnObj}",只要是一个合法的 ognl 表达式,都能被正常支持。

观察的维度也比较多,主要体现在参数 advice 的数据结构上。Advice 参数最主要是封装了通知节点的所有信息。请参考表达式核心变量中关于该节点的描述。

tt

https://arthas.gitee.io/tt.html

方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

其他

使用>将结果重写到日志文件,使用&指令命令是后台运行,session断开不影响任务执行生命周期默认为1天)

jobs:列出所有job

kill:强制终止任务

fg:将暂停的任务拉到前台执行

bg:将暂停的任务放到后台执行

grep:搜索满足条件的结果

plaintext:将命令的结果去除ANSI颜色

wc:按行统计输出结果

options:查看或设置Arthas全局开关

profiler:使用async-profiler对应用采样,生成火焰图

profiler/火焰图

https://arthas.gitee.io/profiler.html

使用async-profiler生成火焰图

参数名称 参数说明
action 要执行的操作
actionArg 属性名模式
[i:] 采样间隔(单位:ns)(默认值:10'000'000,即10 ms)
[f:] 将输出转储到指定路径
[d:] 运行评测指定秒
[e:] 要跟踪哪个事件(cpu, alloc, lock, cache-misses等),默认是cpu

options

https://arthas.gitee.io/options.html

全局开关

名称 默认值 描述
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的事件共有四种类型,它们分别为以下四种。

  1. 瞬时事件(Instant Event),用户关心的是它们发生与否,例如异常、线程启动事件。
  2. 持续事件(Duration Event),用户关心的是它们的持续时间,例如垃圾回收事件。
  3. 计时事件(Timed Event),是时长超出指定阙值的持续事件。
  4. 取样事件(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.startJFR.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等……

posted @ 2021-03-15 14:44  我係死肥宅  阅读(578)  评论(0编辑  收藏  举报