1、JVM监控及诊断工具-命令行篇
1.0 相关问题
- 你使用过Java虚拟机性能监控和故障处理工具吗?(美图)
- 怎么打出线程栈信息。(字节跳动)
- JVM诊断调优工具用过哪些? (京东)
- 怎么获取 Java 程序使用的内存?堆使用的百分比?(国美)
- 几种常用的内存调试工具你知道哪些?(搜狐)
- JVM诊断调优工具用过哪些? (蚂蚁金服)
1.1 概述
简单命令行工具 在我们刚接触java学习的时候,大家肯定最先了解的两个命令就是javac,java,那么除此之外,还有没有其他的命令可以供我们使用呢?我们进入到安装jdk的bin目录,发现还有一系列辅助工具。这些辅助工具用来获取目标 JVM 不同方面、不同层次的信息,帮助开发人员很好地解决Java应用程序的一些疑难杂症。 mac系统下:
windows系统下:
源码: hg.openjdk.java.net/jdk/jdk11/f…
1.2 jps:查看正在运行的Java进程
1.2.1 基本情况
jps(Java Process Status):
显示指定系统内所有的HotSpot虚拟机进程(查看虚拟机进程信息),可用于查询正在运行的虚拟机进程。
说明:对于本地虚拟机进程来说,进程的本地虚拟机ID与操作系统的进程ID是一致的,是唯一的。
1.2.2 测试代码
见尚硅谷大厂学院 - 《JVM与GC调优专题-宋红康》 - 代码部分
1.2.3 基本语法
它的基本使用语法为: jps [options] [hostid]
我们还可以通过追加参数,来打印额外的信息。
1.2.3.1 options参数
-q :仅仅显示LVMID (local virtual machine id),即本地虚拟机唯一id。不显示主类的名称等
-l: 输出应用程序主类的全类名 或 如果进程执行的是jar包,则输出jar完整路径
-m: 输出虚拟机进程启动时传递给主类main()的参数
-v: 列出虚拟机进程启动时的JVM参数。 比如:-Xms20m -Xmx50m是启动程序指定的jvm参数。
说明:以上参数可以综合使用。
补充: 如果某 Java 进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData),那么jps命令(以及下面介绍的jstat)将无法探知该 Java 进程。
1.2.3.2 hostid参数
1.3 jstat:查看JVM统计信息
1.3.1 基本情况
jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
官方文档: docs.oracle.com/javase/8/do…
1.3.2 基本语法
它的基本使用语法为: jstat - [-t] [-h] [ []]
查看命令相关参数: jstat -h 或 jstat -help
1.3.2.1 option参数
选项option可以由以下值构成。
-
类装载相关的:
- -class:显示ClassLoader的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
-
垃圾回收相关的:
- -gc:显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。
- -gccapacity:显示内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间。 -gcutil:显示内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比。
- -gccause:与-gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。
- -gcnew:显示新生代GC状况
- -gcnewcapacity:显示内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
- -geold:显示老年代GC状况
- -gcoldcapacity:显示内容与-gcold基本相同,输出主要关注使用到的最大、最小空间
- -gcpermcapacity:显示永久代使用到的最大、最小空间。
-
JIT相关的:
- -compiler:显示JIT编译器编译过的方法、耗时等信息
- -printcompilation:输出已经被JIT编译的方法
1.3.2.2 interval参数
用于指定输出统计数据的周期,单位为毫秒。即:查询间隔
1.3.2.3 count参数
用于指定查询的总次数
1.3.2.4 -t参数
可以在输出信息前加上一个Timestamp列,显示程序的运行时间。单位:秒
1.3.2.5 -h参数
可以在周期性数据输岀时,输出多少行数据后输出一个表头信息
1.4 jinfo:实时查看和修改JVM配置参数
1.4.1 基本情况
jinfo(Configuration Info for Java) 查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。
在很多情况下,Java应用程序不会指定所有的Java虚拟机参数。而此时,开发人员可能不知道某一个具体的Java虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个査找过程可能是非常艰难的。但有了jinfo工具,开发人员可以很方便地找到Java虚拟机参数的当前值。
官方帮助文档: docs.oracle.com/en/java/jav…
1.4.2 基本语法
它的基本使用语法为: jinfo [ options ] pid
说明:java 进程ID 必须要加上
1.4.2.1 查看
1.4.2.1.1 jinfo -sysprops PID
1.4.2.1.2 jinfo -flags PID
1.4.2.1.3 jinfo -flag 具体参数 PID
1.4.2.2 修改
jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。
但是,并非所有参数都支持动态修改。参数只有被标记为manageable的flag可以被实时修改。其实,这个修改能力是极其有限的。
可以查看被标记为manageable的参数 java -XX:+PrintFlagsFinal -version | grep manageable
intx CMSAbortablePrecleanWaitMillis = 100 {manageable}
intx CMSWaitDuration = 2000 {manageable}
bool HeapDumpAfterFullGC = false {manageable}
bool HeapDumpBeforeFullGC = false {manageable}
bool HeapDumpOnOutOfMemoryError = false {manageable}
ccstr HeapDumpPath = {manageable}
uintx MaxHeapFreeRatio = 100 {manageable}
uintx MinHeapFreeRatio = 0 {manageable}
bool PrintClassHistogram = false {manageable}
bool PrintClassHistogramAfterFullGC = false {manageable}
bool PrintClassHistogramBeforeFullGC = false {manageable}
bool PrintConcurrentLocks = false {manageable}
bool PrintGC = false {manageable}
bool PrintGCDateStamps = false {manageable}
bool PrintGCDetails = false {manageable}
bool PrintGCTimeStamps = false {manageable}
1.4.2.2.1 针对boolean类型
jinfo -flag [+|-]具体参数 PID
1.4.2.2.2 针对boolean类型
jinfo -flag 具体参数=具体参数值 PID
1.5 jmap:导出内存映像文件&内存使用情况
1.5.1 基本情况
jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。
开发人员可以在控制台中输入命令“jmap -help”查阅jmap工具的具体使用方式和一些标准选项配置。
官方帮助文档: docs.oracle.com/en/java/jav…
1.5.2 基本语法
它的基本使用语法为:
- jmap [option]
- jmap [option] <executable
- jmap [option] [server_id@]
其中option包括:
说明:这些参数和linux下输入显示的命令多少会有不同,包括也受jdk版本的影响。
1.5.2.1 -dump
- 生成Java堆转储快照:dump文件
- 特别的:-dump:live只保存堆中的存活对象
1.5.2.2 -heap
输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等
1.5.2.3 -histo
- 输出堆中对象的统计信息,包括类、实例数量和合计容量
- 特别的:-histo:live只统计堆中的存活对象
1.5.2.4 -permstat
- 以ClassLoader为统计口径输出永久代的内存状态信息
- 仅linux/solaris平台有效
1.5.2.5 -finalizerinfo
- 显示在F-Queue中等待Finalizer线程执行finalize方法的对象
- 仅linux/solaris平台有效
1.5.2.6 -F
- 当虚拟机进程对-dump选项没有任何响应时,可使用此选项强制执行生成dump文件
- 仅linux/solaris平台有效
1.5.2.7 -h | -help
jmap工具使用的帮助命令
1.5.2.7 -J
传递参数给jmap启动的jvm
1.5.3 使用1:导出内存映像文件
一般来说,使用jmap指令生成dump文件的操作算得上是最常用的jmap命令之一,将堆中所有存活对象导出至一个文件之中。
Heap Dump又叫做堆存储文件,指一个Java进程在某个时间点的内存快照。Heap Dump在触发内存快照的时候会保存此刻的信息如下:
-
All Objects
- Class,fields,primitive values and references
-
All Classes
- ClassLoader,name,super class,static fields
-
Garbage Collection Roots
- Objects defined to be reachable by the JVM
-
Thread Stacks and Local Variables
- The call-stacks of threads at the moment of the snapshot,and per-frame information about local objects
说明:
- 通常在写Heap Dump文件前会触发一次Full GC,所以heap dump文件里保存的都是FullGC后留下的对象信息。
- 由于生成dump文件比较耗时,因此大家需要耐心等待,尤其是大内存镜像生成dump文件则需要耗费更长的时间来完成。
1.5.3.1 手动方式
-XX:+HeapDumpOnOutOfMemoryError jmap -dump:format=b,file=<filename.hprof>
1.5.3.2 自动方式
当程序发生OOM退出系统时,一些瞬时信息都随着程序的终止而消失,而重现OOM问题往往比较困难或者耗时。此时若能在OOM时,自动导出dump文件就显得非常迫切。
这里介绍一种比较常用的取得堆快照文件的方法,即使用:
- -XX:+HeapDumpOnOutOfMemoryError:在程序发生OOM时,导出应用程序的当前堆快照。
- -XX:HeapDumpPath:可以指定堆快照的保存位置。
比如: -Xmx100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\m.hprof
- jmap -dump:live,format=b,file=<filename.hprof>
- -XX:HeapDumpPath=<filename.hprof>
1.5.4 使用2:显示堆内存相关信息
- jmap -heap pid
- -jmap -histo pid
1.5.5 使用3:其它作用
1.5.5.1 jmap -permstat pid
查看系统的ClassLoader信息
1.5.5.2 jmap -finalizerinfo
査看堆积在finalizer队列中的对象
1.6 jhat:JDK自带堆分析工具
1.6.1 基本情况
jhat(JVM Heap Analysis Tool):
Sun JDK提供的jhat命令与jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照)。jhat内置了一个微型的HTTP/HTML服务器, 生成dump文件的分析结果后, 用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。
使用了jhat命令,就启动了一个http服务,端口是7000,即http://localhost:7000/,就可以在浏览器里分析。
说明:jhat命令在JDK9、JDK10中已经被删除,官方建议用VisualVM代替。
1.6.2 基本语法
它的基本使用语法为: jhat [option] [dumpfile]
-
option参数:-stack false|true
- 关闭 | 打开对象分配调用栈跟踪
-
option参数:-refs false|true
- 关闭 | 打开对象引用跟踪
-
option参数:-port port-number
- 设置jhat HTTP Server的端口号,默认7000
-
option参数:-exclude exclude-file
- 执行对象查询时需要排除的数据成员列表文件
-
option参数:-baseline exclude-file
- 指定一个基准堆转储
-
option参数:-debug int
- 设置debug级别
-
option参数:-version
- 启动后显示版本信息就退出
-
option参数:-J
- 传入启动参数,比如-J -Xmx512m`
1.7 jstack:打印JVM中线程快照
1.7.1 相关问题
- JVM的进程有哪些线程启动,有了解吗?(神州租车)
每当使用java命令执行一个带main方法的类时,就会启动JVM(应用程序),实际上就是**在操作系统中启动一个JVM进程,JVM启动时,必然会创建以下5个线程:**
1-main 主线程,执行我们指定的启动类的main方法
2-Reference Handler 处理引用的线程
3-Finalizer 调用对象的finalize方法的线程,就是垃圾回收的线程
4-Signal Dispatcher 分发处理发送给JVM信号的线程
5-Attach Listener 负责接收外部的命令的线程
Attach Listener :该线程是负责接收到外部的命令,执行该命令,并且把结果返回给发送者。通常我们会用一些命令去要求jvm给我们一些反馈信息,如:java -version、jmap、jstack等等。如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动。
signal dispather: 前面我们提到第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作。
Finalizer:JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收。
Reference Handler :它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。
main:主线程,用于执行我们编写的java程序的main方法。
编写java应用程序查看JVM启动时创建的所有线程,代码如下:
package com.atguigu.java;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
public class JVMTest {
public static void main(String[] args) throws Exception {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for(ThreadInfo threadInfo : threadInfos) {
System.out.println(threadInfo.getThreadId() + "-" + threadInfo.getThreadName());
}
}
}
输出如下:
5-Attach Listener 4-Signal Dispatcher 3-Finalizer 2-Reference Handler 1-main
1.7.2 基本情况
它的基本使用语法为: jstack option pid
jstack管理远程进程的话,需要在远程程序的启动参数中增加: -Djava.rmi.server.hostname=….. -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
1.7.3 基本语法
-
option参数:-F
- 当正常输出的请求不被响应时,强制输出线程堆栈
-
option参数:-l
- 除堆栈外,显示关于锁的附加信息
-
option参数:-m
- 如果调用到本地方法的话,可以显示C/C++的堆栈
-
option参数:-h
- 帮助操作
1.8 jcmd:多功能命令行
1.8.1 基本情况
在JDK 1.7以后,新增了一个命令行工具jcmd。 它是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。
官方帮助文档: docs.oracle.com/en/java/jav…
jcmd拥有jmap的大部分功能,并且在Oracle的官方网站上也推荐使用jcmd命令代jmap命令。
1.8.2 基本语法
1.8.2.1 jcmd -l
列出所有的JVM进程
1.8.2.2 jcmd pid help
针对指定的进程,列出支持的所有命令
1.8.2.3 jcmd pid 具体命令
- 显示指定进程的指令命令的数据
你可以探索jcmd中的下述功能,看看有没有适合你项目的监控项:
Compiler.CodeHeap_Analytics
Compiler.codecache
Compiler.codelist
Compiler.directives_add
Compiler.directives_clear
Compiler.directives_print
Compiler.directives_remove
Compiler.queue
GC.class_histogram
GC.class_stats
GC.finalizer_info
GC.heap_dump
GC.heap_info
GC.run
GC.run_finalization
VM.class_hierarchy
VM.classloader_stats
VM.classloaders
VM.command_line
VM.dynlibs
VM.flags
VM.info
VM.log
VM.metaspace
VM.native_memory
VM.print_touched_methods
VM.set_flag
VM.stringtable
VM.symboltable
VM.system_properties
VM.systemdictionary
VM.unlock_commercial_features
VM.uptime
VM.version
1.9 jstatd:远程主机信息收集
之前的指令只涉及到监控本机的Java应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如jps、jstat)。为了启用远程监控,则需要配合使用jstatd 工具。
命令jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。 jstatd服务器将本机的Java应用程序信息传递到远程计算机。
2、JVM监控及诊断工具-GUI篇
2.1 工具概述
使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限:
- 无法获取方法级别的分析数据,如方法间的调用关系、各方法的调用次数和调用时间等(这对定位应用性能瓶颈至关重要)。
- 要求用户登录到目标 Java 应用所在的宿主机上,使用起来不是很方便。
- 分析数据通过终端输出,结果展示不够直观。
为此,JDK提供了一些内存泄漏的分析工具,如jconsole,jvisualvm等,用于辅助开发人员定位问题,但是这些工具很多时候并不足以满足快速定位的需求。所以这里我们介绍的工具相对多一些、丰富一些。
图形化综合诊断工具
-
JDK自带的工具
-
jconsole:JDK自带的可视化监控工具。查看Java应用程序的运行概况、监控堆信息、永久区(或元空间)使用情况、类加载情况等
位置:jdk\bin\jconsole.exe
-
Visual VM:Visual VM是一个工具,它提供了一个可视界面,用于查看Java虚拟机上运行的基于Java技术的应用程序的详细信息。
位置:jdk\bin\jvisualvm.exe
-
JMC:Java Mission Control,内置Java Flight Recorder。能够以极低的性能开销收集Java虚拟机的性能数据。
-
-
第三方工具
-
MAT: MAT(Memory Analyzer Tool)是基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
Eclipse的插件形式
-
JProfiler:商业软件,需要付费。功能强大。
与 VisualVM类似
-
Arthas:Alibaba开源的Java诊断工具。深受开发者喜爱。
-
Btrace:Java运行时追踪工具。可以在不停机的情况下,跟踪指定的方法调用、构造函数调用和系统内存等信息。
-
2.2 jConsole
2.2.1 基本概述
jconsole: 从Java5开始,在JDK中自带的java监控和管理控制台。 用于对JVM中内存、线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监控工具。
官方教程: docs.oracle.com/javase/7/do…
2.2.2 启动
- jdk/bin目录下,启动jconsole.exe命令即可
- 不需要使用jps命令来查询
2.2.3 三种连接方式
- Local
- Remote
- Advanced
2.2.4 主要作用
- 监控内存
- 监控线程
- 监控死锁
- 类加载与虚拟机信息
2.3 Visual VM
2.3.1 基本概述
Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具。 它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替JConsole。 在JDK 6 Update 7以后,Visual VM便作为JDK的一部分发布(VisualVM 在JDK/bin目录下),即:它完全免费。 此外,Visual VM也可以作为独立的软件安装: 首页:visualvm.github.io/index.html
2.3.2 插件的安装
-
Visual VM的一大特点是支持插件扩展,并且插件安装非常方便。我们既可以通过离线下载插件文件*.nbm,然后在Plugin对话框的已下载页面下,添加已下载的插件。也可以在可用插件页面下,在线安装插件。(这里建议安装上:VisualGC)
-
插件地址:visualvm.github.io/pluginscent…
-
IDEA安装VisualVM Launcher插件
Preferences --> Plugins --> 搜索VisualVM Launcher,安装重启即可。
- ① 在IDEA中安装插件:
- ② 配置Idea VisualVM Launcher插件
2.3.3 连接方式
- 本地连接
- 远程连接
2.3.4 主要功能
-
1.生成/读取堆内存快照
-
2.查看JVM参数和系统属性
-
3.查看运行中的虚拟机进程
-
4.生成/读取线程快照
-
5.程序资源的实时监控
-
6.其他功能
- JMX代理连接
- 远程环境监控
- CPU分析和内存分析
2.4 eclipse MAT
2.4.1 基本概述
MAT(Memory Analyzer Tool)工具是一款功能强大的Java堆内存分析器。可以用于查找内存泄漏以及查看内存消耗情况。
MAT是基于Eclipse开发的,不仅可以单独使用,还可以作为插件的形式嵌入在Eclipse中使用。是一款免费的性能分析工具,使用起来非常方便。大家可以在www.eclipse.org/mat/downloa…下载并使用MAT。
只要确保机器上装有JDK并配置好相关的环境变量,MAT可正常启动。 还可以在Eclipse中以插件的方式安装:
2.4.2 获取堆dump文件
2.4.2.1 两点说明
- 说明1:缺点:
MAT 不是一个万能工具,它并不能处理所有类型的堆存储文件。但是比较主流的厂家和格式,例如 Sun, HP, SAP 所采用的 HPROF 二进制堆存储文件,以及 IBM 的 PHD 堆存储文件等都能被很好的解析。
- 说明2:
最吸引人的还是能够快速为开发人员生成内存泄漏报表,方便定位问题和分析问题。虽然MAT有如此强大的功能,但是内存分析也没有简单到一键完成的程度,很多内存问题还是需要我们从MAT展现给我们的信息当中通过经验和直觉来判断才能发现。
2.4.2.2 获取dump文件
方法一:通过前一章介绍的 jmap工具生成,可以生成任意一个java进程的dump文件;
方法二:通过配置JVM参数生成。
- 选项"-XX:+HeapDumpOnOutOfMemoryError" 或 "-XX:+HeapDumpBeforeFullGC"
- 选项"-XX:HeapDumpPath"所代表的含义就是当程序出现OutofMemory时,将会在相应的目录下生成一份dump文件。如果不指定选项“-XX:HeapDumpPath”则在当前目录下生成dump文件。
对比:考虑到生产环境中几乎不可能在线对其进行分析,大都是采用离线分析,因此使用jmap+MAT工具是最常见的组合。
方法三:使用VisualVM可以导出堆dump文件
方法四: 使用MAT既可以打开一个已有的堆快照,也可以通过MAT直接从活动Java程序中导出堆快照。 该功能将借助jps列出当前正在运行的 Java 进程,以供选择并获取快照。
2.4.3 分析堆dump文件
2.4.3.1 histogram
MAT 的直方图和jmap的-histo子命令一样,都能够展示各个类的实例数目以及这些实例的 Shallow heap 总和。但是,MAT 的直方图还能够计算 Retained heap,并支持基于实例数目或 Retained heap 的排序方式(默认为 Shallow heap)。
此外,MAT 还可以将直方图中的类按照超类、类加载器或者包名分组。
当选中某个类时,MAT 界面左上角的 Inspector 窗口将展示该类的 Class 实例的相关信息,如类加载器等。
- 展示了各个类的实例数目以及这些实例的Shallow heap 或Retainedheap的总和
2.4.3.2 thread overview
- 查看系统中的Java线程
- 查看局部变量的信息
2.4.3.3 获得对象相互引用的关系
- with outgoing references
- with incoming references
2.4.3.4 浅堆与深堆
2.4.3.5 支配树
支配树(Dominator Tree) 支配树的概念源自图论。
MAT提供了一个称为支配树(Dominator Tree)的对象图。支配树体现了对象实例间的支配关系。在对象引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。支配树是基于对象间的引用图所建立的,它有以下基本性质:
- 对象A的子树(所有被对象A支配的对象集合)表示对象A的保留集(retained set),即深堆。
- 如果对象A支配对象B,那么对象A的直接支配者也支配对象B。
- 支配树的边与对象引用图的边不直接对应。
如下图所示:左图表示对象引用图,右图表示左图所对应的支配树。对象A和B由根对象直接支配,由于在到对象C的路径中,可以经过A,也可以经过B,因此对象C的直接支配者也是根对象。对象F与对象D相互引用,因为到对象F的所有路径必然经过对象D,因此,对象D是对象F的直接支配者。而到对象D的所有路径中,必然经过对象C,即使是从对象F到对象D的引用,从根节点出发,也是经过对象C的,所以,对象D的直接支配者为对象C。
同理,对象E支配对象G。到达对象H的可以通过对象D,也可以通过对象E,因此对象D和E都不能支配对象H,而经过对象C既可以到达D也可以到达E,因此对象C为对象H的直接支配者。
在MAT中,单击工具栏上的对象支配树按钮,可以打开对象支配树视图。
下图显示了对象支配树视图的一部分。该截图显示部分Lily学生的history队列的直接支配对象。即当Lily对象被回收,也会一并回收的所有对象。显然能被3或者5整除的网页不会出现在该列表中,因为它们同时被另外两名学生对象引用。
2.4.4 案例:Tomcat堆溢出分析
2.4.4.1 说明
Tomcat是最常用的Java Servlet容器之一,同时也可以当做单独的Web服务器使用。Tomcat本身使用Java实现,并运行于Java虚拟机之上。在大规模请求时,Tomcat有可能会因为无法承受压力而发生内存溢出错误。这里根据一个被压垮的Tomcat的堆快照文件,来分析Tomcat在崩溃时的内部情况。
2.4.4.2 分析过程
图1:
图2:
图3:sessions对象,它占用了约17MB空间
图4:可以看到sessions对象为ConcurrentHashMap,其内部分为16个Segment。从深堆大小看,每个Segment都比较平均,大约为1MB,合计17MB。
图5:
图6:当前堆中含有9941个session,并且每一个session的深堆为1592字节,合计约15MB,达到当前堆大小的50%。
图7:
图8:
根据当前的session总数,可以计算每秒的平均压力为:9941/(1403324677648-1403324645728)*1000=311次/秒。
由此推断,在发生Tomcat堆溢出时,Tomcat在连续30秒的时间内,平均每秒接收了约311次不同客户端的请求,创建了合计9941个session。
2.5 JProfiler
2.5.1 基本概述
2.5.1.1 介绍
在运行Java的时候有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在eclipse里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试,而在IDEA中也有这么一个插件,就是JProfiler。
JProfiler 是由 ej-technologies 公司开发的一款 Java 应用性能诊断工具。功能强大,但是收费。
官网下载地址:www.ej-technologies.com/products/jp…
2.5.1.2 特点
特点:
- 使用方便、界面操作友好 (简单且强大)
- 对被分析的应用影响小 (提供模板)
- CPU,Thread,Memory分析功能尤其强大
- 支持对jdbc,noSql, jsp, servlet, socket等进行分析
- 支持多种模式(离线,在线)的分析
- 支持监控本地、远程的JVM
- 跨平台,拥有多种操作系统的安装版本
2.5.1.3 主要功能
- 1-方法调用
- 2-内存分配
- 3-线程和锁
- 4-高级子系统
2.5.2 安装与配置
2.5.2.1 下载与安装
下载: www.ej-technologies.com/download/jp…
安装后,直接启动bin目录下jprofiler.exe命令即可
2.5.2.2 JProfiler中配置IDEA
选择IDEA 2019
2.5.2.3 IDEA集成JProfiler
一、安装分为在线安装和离线安装两种方式 方式一: 直接在IDEA上下载File–Settings–plugins–Browse repositories ,找到jprofiler然后点击安装。
看到如下图片则说明安装完成
方式二: 从官网下载插件 官方下载地址:plugins.jetbrains.com/plugin/253-…
找到对应的版本下载就行。 然后把从下载的压缩包解压出来的JProfiler文件夹,copy到IDEA自定义插件目录,默认路径:C:\Users\Administrator.IntelliJIdea2017.2\config\plugins
二、安装完之后,还不能使用,如果不配置OK的话,会一直报错的。
然后启动的时候,就可以使用这个 jprofiler 的插件启动,监测了。
启动项目的时候,他会自动调用你安装的客户端。
2.5.3 具体使用
2.5.3.1 数据采集方式
JProfier数据采集方式分为两种:Sampling(样本采集)和Instrumentation(重构模式)
-
Instrumentation: 这是JProfiler全功能模式。在class加载之前,JProfier把相关功能代码写入到需要分析的class的bytecode中,对正在运行的jvm有一定影响。
- 优点: 功能强大。在此设置中,调用堆栈信息是准确的。
- 缺点:若要分析的class较多,则对应用的性能影响较大,CPU开销可能很高(取决于Filter的控制)。因此使用此模式一般配合Filter使用,只对特定的类或包进行分析。
-
Sampling: 类似于样本统计, 每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来。
- 优点:对CPU的开销非常低,对应用影响小(即使你不配置任何Filter)
- 缺点:一些数据/特性不能提供(例如:方法的调用次数、执行时间)
注: JProfiler本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型。因为JProfiler的绝大多数核心功能都依赖方法调用采集的数据, 所以可以直接认为是JProfiler的数据采集类型。
2.5.3.2 遥感监测 Telemetries
遥感监测 Telemetries(查看JVM的运行信息)
- 整体视图 Overview:显示堆内存、cpu、线程以及GC等活动视图
- 内存 Memory:显示一张关于内存变化的活动时间表。
- 记录的对象 Recorded objects:显示一张关于活动对象与数组的图表的活动时间表。
- 记录吞吐量 Record Throughput:显示一段时间累计的JVM生产和释放的活动时间表。
- 垃圾回收活动 GC Activity:显示一张关于垃圾回收活动的活动时间表。
- 类 Classes:显示一个与已装载类的图表的活动时间表。
- 线程 Threads:显示一个与动态线程图表的活动时间表
- CPU负载 CPU Load:显示一段时间中CPU的负载图表。
2.5.3.3 内存视图 Live Memory
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[]过大
- 存在内存泄漏
2.5.3.4 堆遍历 heap walker
Heap walker 堆遍历 :对一定时间内收集的内存对像信息进行静态分析,功能强大且使用。包含对象的outgoing reference, incoming reference, biggest object等。
类 Classes 显示所有类和它们的实例,可以右击具体的类"Used Selected Instance"实现进一步跟踪。 分配 Allocations 为所有记录对象显示分配树和分配热点。 索引 References 为单个对象和“显示到垃圾回收根目录的路径”提供索引图的显示功能。还能提供合并输入视图和输出视图的功能。 时间 Time 显示一个对已记录对象的解决时间的柱状图。 检查 Inspections 显示了一个数量的操作,将分析当前对象集在某种条件下的子集,实质是一个筛选的过程。 图表 Graph 你需要在references视图和biggest视图手动添加对象到图表,它可以显示对象的传入和传出引用,能方便的找到垃圾收集器根源。
Ps:在工具栏点击"Go To Start"可以使堆内存重新计数,也就是回到初始状态。
2.5.3.5 cpu视图 cpu views
JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。 访问树 Call Tree 显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。JDBC,JMS和JNDI服务请求都被注释在请求树中。请求树可以根据Servlet和JSP对URL的不同需要进行拆分。 热点 Hot Spots 显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC,JMS和JNDI服务请求以及按照URL请求来进行计算。 访问图 Call Graph 显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。 方法统计 Method Statistis 显示一段时间内记录的方法的调用时间细节。
2.5.3.6 线程视图 threads
JProfiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析。 线程历史 Thread History 显示一个与线程活动和线程状态在一起的活动时间表。 线程监控 Thread Monitor 显示一个列表,包括所有的活动线程以及它们目前的活动状况。 线程转储 Thread Dumps 显示所有线程的堆栈跟踪。
线程分析主要关心三个方面:
- web容器的线程最大数。比如:Tomcat的线程容量应该略大于最大并发数。
- 线程阻塞
- 线程死锁
2.5.3.7 监视器&锁 Monitors&locks
监控和锁 Monitors & Locks 所有线程持有锁的情况以及锁的信息。 观察JVM的内部线程并查看状态:
- 死锁探测图表 Current Locking Graph :显示JVM中的当前死锁图表。
- 目前使用的监测器 Current Monitors :显示目前使用的监测器并且包括它们的关联线程。
- 锁定历史图表 Locking History Graph :显示记录在JVM中的锁定历史。
- 历史检测记录 Monitor History :显示重大的等待事件和阻塞事件的历史记录。
- 监控器使用统计 Monitor Usage Statistics :显示分组监测,线程和监测类的统计监测数据。
2.5.4 案例分析
2.6 Arthas
2.6.1 基本概述
2.6.1.1 背景
前面,我们介绍了jdk自带的jvisualvm等免费工具,以及商业化工具Jprofiler。 jvisualvm界面
Jprofiler
这两款工具在业界知名度也比较高,他们的优点是可以图形界面上看到各维度的性能数据,使用者根据这些数据进行综合分析,然后判断哪里出现了性能问题。
但是这两款工具也有个缺点,都必须在服务端项目进程中配置相关的监控参数。然后工具通过远程连接到项目进程,获取相关的数据。这样就会带来一些不便,比如线上环境的网络是隔离的,本地的监控工具根本连不上线上环境。并且类似于Jprofiler这样的商业工具,是需要付费的。 那么有没有一款工具不需要远程连接,也不需要配置监控参数,同时也提供了丰富的性能监控数据呢?
今天跟大家介绍一款阿里巴巴开源的性能分析神器Arthas(阿尔萨斯)
2.6.1.2 概述
Arthas(阿尔萨斯) 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。
Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
2.6.1.3 基于哪些工具开发而来
- 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字节码框架类似的功能,但是主要关注性能。因为它被设计和实现得尽可能小和快,所以非常适合在动态系统中使用(当然也可以以静态方式使用,例如在编译器中)
2.6.1.4 官方使用文档
2.6.2 安装与使用
2.6.2.1 安装
安装方式一:可以直接在Linux上通过命令下载 可以在官方 Github 上进行下载,如果速度较慢,可以尝试国内的码云 Gitee 下载。
-
github下载
-
Gitee 下载
安装方式二: 也可以在浏览器直接访问alibaba.github.io/arthas/arth…,等待下载成功后,上传到Linux服务器上。
卸载: 在 Linux/Unix/Mac 平台 删除下面文件: rm -rf ~/.arthas/ rm -rf ~/logs/arthas Windows平台直接删除user home下面的.arthas和logs/arthas目录
2.6.2.2 工程目录
arthas-agent:基于JavaAgent技术的代理 bin:一些启动脚本 arthas-boot:Java版本的一键安装启动脚本 arthas-client:telnet client代码 arthas-common:一些共用的工具类和枚举类 arthas-core:核心库,各种arthas命令的交互和实现 arthas-demo:示例代码 arthas-memorycompiler:内存编绎器代码,Fork from github.com/skalogs/Ska… arthas-packaging:maven打包相关的 arthas-site:arthas站点 arthas-spy:编织到目标类中的各个切面 static:静态资源 arthas-testcase:测试
2.6.2.3 启动
Arthas 只是一个 java 程序,所以可以直接用 java -jar 运行。
执行成功后,arthas提供了一种命令行方式的交互方式,arthas会检测当前服务器上的Java进程,并将进程列表展示出来,用户输入对应的编号(1、2、3、4…)进行选择,然后回车。 比如:方式1: java -jar arthas-boot.jar
选择进程(输入[]内编号(不是PID)回车)
[INFO] arthas-boot version: 3.1.4 [INFO] Found existing java process, please choose one and hit RETURN. [1]: 11616 com.Arthas [2]: 8676 [3]: 16200 org.jetbrains.jps.cmdline.Launcher [4]: 21032 org.jetbrains.idea.maven.server.RemoteMavenServer
方式2:运行时选择 Java 进程 PID java -jar arthas-boot.jar [PID]
2.6.2.4 查看进程
查看 PID 的方式可以通过 ps 命令,也可以通过 JDK 提供的 jps命令。
- 查看运行的 java 进程信息 jps -mlvV ps -ef| grep java
- 筛选 java 进程信息 jps -mlvV | grep [xxx]
2.6.2.5 查看日志
cat ~/logs/arthas/arthas.log
2.6.2.6 参看帮助
java -jar arthas-boot.jar -h
2.6.2.7 web console
除了在命令行查看外,Arthas 目前还支持 Web Console。在成功启动连接进程之后就已经自动启动,可以直接访问 http://127.0.0.1:8563/ 访问,页面上的操作模式和控制台完全一样。
2.6.2.8 退出
最后一行 [arthas@7457]$,说明打开进入了监控客户端,在这里就可以执行相关命令进行查看了。
- 使用quit\exit:退出当前客户端
- 使用stop\shutdown:关闭arthas服务端,并退出所有客户端。
2.6.3 相关诊断指令
2.6.3.1 基础指令
2.6.3.2 jvm相关
2.6.3.2.1 dashboard
dashboard命令:可以查看当前系统的实时数据面板。
-
展示当前tomcat的多线程状态、JVM各区域、GC情况等信息
-
输入 Q 或者 Ctrl+C 可以退出dashboard命令
-
常用参数:
- -i1000:每次执行间隔时间,这是单位是毫秒
- -n4:执行多少次dashboard,不指定的话会一直刷新
可以看到,这里会显示出线程(按照cpu占用百分比倒排)、内存(堆空间实时情况)、GC情况等数据。
2.6.3.2.2 thread
thread命令:查看当前 JVM 的线程堆栈信息
-
常用参数:
- 无 :显示所有线程的状态信息
- -b 显示当前处于BLOCKED状态的线程,可以排查线程锁的问题
- -i 5000 查看在接下来的多长时间内统计cpu利用率,单位毫秒
- -n 5 查看cpu占用率前5的线程的堆栈信息
- <thread_id> 直接跟着线程id,可以看到指定thread的堆栈信息
-
Arthas支持管道,可以用 thread 1 | grep 'main(' 查找到main class。
[arthas@49290]
2.6.3.2.3 jvm
jvm命令:查看jvm详细的性能数据 arthas.aliyun.com/doc/jvm
2.6.3.2.4 其它
sysprop 查看和修改JVM的系统属性 arthas.aliyun.com/doc/sysprop
sysenv 查看JVM的环境变量 arthas.aliyun.com/doc/sysenv
getstatic 查看类的静态属性 arthas.aliyun.com/doc/getstat…
heapdump
-
类似 jmap 命令的 heap dump 功能
-
dump到指定的文件:
- [arthas@58205]$ heapdump /tmp/dump.hprof
-
只dump live对象:
- [arthas@58205]$ heapdump --live /tmp/dump.hprof
2.6.3.3 class/classloader相关
2.6.3.3.1 sc
sc命令:查看 JVM 已加载的类信息
-
常用参数:
- class-pattern 类名表达式匹配
- -d 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次
- -E 开启正则表达式匹配,默认为通配符匹配
- -f 输出当前类的成员变量信息(需要配合参数-d一起使用)
- -x 指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出
-
补充:
- class-pattern支持全限定名,如com.test.AAA,也支持com/test/AAA这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把/替换为.了。
- sc 默认开启了子类匹配功能,也就是说所有当前类的子类也会被搜索出来,想要精确的匹配,请打开options disable-sub-class true开关
sc -d com.atguigu.arthas.OOMTest
2.6.3.3.2 sm
sm命令:查看已加载类的方法信息
-
sm 命令只能看到由当前类所声明 (declaring) 的方法,父类则无法看到。
-
常用参数:
- class-pattern 类名表达式匹配
- method-pattern 方法名表达式匹配
- -d 展示每个方法的详细信息
- -E 开启正则表达式匹配,默认为通配符匹配
sm -d com.atguigu.arthas.OOMTest main
2.6.3.3.3 jad
jad命令:反编译指定已加载类的源码
- arthas.aliyun.com/doc/jad
- 在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便
- 当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解
- 编译java.lang.String
2.6.3.3.4 mc 、redefine
mc命令:Memory Compiler/内存编译器,编译.java文件生成.class arthas.aliyun.com/doc/mc
mc /tmp/Test.java
redefine命令:加载外部的.class文件,redefine jvm已加载的类。 arthas.aliyun.com/doc/redefin… 推荐使用 retransform 命令
redefine /tmp/Test.class
redefine -c 327a647b /tmp/Test.class /tmp/Test$Inner.class
2.6.3.3.5 classloader
classloader命令:查看 classloader 的继承树,urls,类加载信息
-
了解当前系统中有多少类加载器,以及每个加载器加载的类数量,帮助您判断是否有类加载器泄漏。
-
常用参数:
- -t : 查看ClassLoader的继承树
- -l : 按类加载实例查看统计信息
- -c : 用classloader对应的hashcode 来查看对应的jar urls
2.6.3.4 monitor/watch/trace相关
2.6.3.4.1 monitor
monitor命令:方法执行监控
-
对匹配 class-pattern/method-pattern的类、方法的调用进行监控。涉及方法的调用次数、执行时间、失败率等
-
monitor 命令是一个非实时返回命令
-
常用参数:
- class-pattern 类名表达式匹配
- method-pattern 方法名表达式匹配
- -c 统计周期,默认值为120秒
-
监控项:
2.6.3.4.2 watch
watch命令:方法执行数据观测
-
让你能方便的观察到指定方法的调用情况。能观察到的范围为:返回值、抛出异常、入参,通过编写 groovy 表达式进行对应变量的查看。
-
常用参数:
- class-pattern 类名表达式匹配
- method-pattern 方法名表达式匹配
- express 观察表达式
- condition-express 条件表达式
- -b 在方法调用之前观察(默认关闭)
- -e 在方法异常之后观察(默认关闭)
- -s 在方法返回之后观察(默认关闭)
- -f 在方法结束之后(正常返回和异常返回)观察 (默认开启)
- -x 指定输出结果的属性遍历深度,默认为0
cost 方法执行耗时
-
说明:这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写"{params,returnObj}",只要是一个合法的 ognl 表达式,都能被正常支持。
-
举例:watch 全限定类名 方法名 returnObj
2.6.3.4.3 trace
trace命令:方法内部调用路径,并输出方法路径上的每个节点上耗时
-
补充说明:
- trace 命令能主动搜索 class-pattern/method-pattern 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。
- trace 能方便的帮助你定位和发现因 RT 高而导致的性能问题缺陷,但其每次只能跟踪一级方法的调用链路
- trace 在执行的过程中本身是会有一定的性能开销,在统计的报告中并未像 JProfiler 一样预先减去其自身的统计开销。所以这统计出来有些许的不准,渲染路径上调用的类、方法越多,性能偏差越大。但还是能让你看清一些事情的。
-
参数说明
- class-pattern 类名表达式匹配
- method-pattern 方法名表达式匹配
- condition-express 条件表达式
- -n 命令执行次数
cost 方法执行耗时
--skipJDKMethod false 设置trace是否包含jdk里的函数调用。默认为false
2.6.3.4.4 stack
stack命令:输出当前方法被调用的调用路径
-
常用参数
- class-pattern 类名表达式匹配
- method-pattern 方法名表达式匹配
- condition-express 条件表达式
- -n 执行次数限制
cost 方法执行耗时
举例:
2.6.3.4.5 tt
tt命令:方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。
-
TimeTunnel的缩写
-
常用参数:
- -t 表明希望记录下类 *Test 的 print 方法的每次执行情况。
- -n 3 指定你需要记录的次数,当达到记录次数时 Arthas 会主动中断tt命令的记录过程,避免人工操作无法停止的情况。
- -s 筛选指定方法的调用信息
- -i 参数后边跟着对应的 INDEX 编号查看到它的详细信息
- -p 重做一次调用 通过 --replay-times 指定 调用次数,通过 --replay-interval 指定多次调用间隔(单位ms, 默认1000ms)
-
举例:
- 表格中的字段说明:
2.6.3.5 其它
使用 > 将结果重写到日志文件,使用&指令命令是后台运行,session断开不影响任务执行(生命周期默认为1天)
- jobs:列出所有job
- kill:强制终止任务
- fg:将暂停的任务拉到前台执行
- bg:将暂停的任务放到后台执行
- grep:搜索满足条件的结果
- plaintext:将命令的结果去除ANSI颜色
- wc:按行统计输出结果
- options:查看或设置Arthas全局开关
- profiler:使用async-profiler对应用采样,生成火焰图
2.6.3.5.1 火焰图
profiler命令:支持生成应用热点的火焰图。
-
arthas.aliyun.com/doc/profile… 本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。
-
参数说明: action 要执行的操作 actionArg 属性名模式 [i:] 采样间隔(单位:ns)(默认值:10'000'000,即10 ms) [f:] 将输出转储到指定路径 [d:] 运行评测指定秒 [e:] 要跟踪哪个事件(cpu, alloc, lock, cache-misses等),默认是cpu
-
启动profiler profiler start
-
获取已采集的sample的数量 profiler getSamples
-
查看profiler状态 profiler status
-
停止profiler,生成svg格式结果 profiler stop 默认情况下,生成的结果保存到应用的工作目录下的arthas-output目录。可以通过 --file参数来指定输出结果路径。
比如:profiler stop --file /tmp/output.svg
-
生成html格式结果 profiler stop --format html
-
通过浏览器查看arthas-output下面的profiler结果 默认情况下,arthas使用3658端口,则可以打开: http://localhost:3658/arthas-output/ 查看到arthas-output目录下面的profiler结果:
点击可以查看具体的结果:
2.6.3.5.2 options
2.7 Java Mission Control
2.7.1 历史
在 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中发布。
自 Java 11 开始,本节介绍的 JFR 已经开源。但在之前的 Java 版本,JFR 属于 Commercial Feature,需要通过 Java 虚拟机参数-XX:+UnlockCommercialFeatures开启。
如果你有兴趣请可以查看OpenJDK的Mission Control项目。 github.com/JDKMissionC…
2.7.2 启动
Mission Control 位于%JAVA_HOME%/ bin/jmc.exe,打开这款软件。
2.7.3 概述
Java Mission Control(简称 JMC),Java官方提供的性能强劲的工具。是一个用于对 Java 应用程序进行管理、监视、概要分析和故障排除的工具套件。
它包含一个 GUI 客户端,以及众多用来收集 Java 虚拟机性能数据的插件,如 JMX Console(能够访问用来存放虚拟机各个子系统运行数据的MXBeans),以及虚拟机内置的高效 profiling 工具 Java Flight Recorder(JFR)。
JMC 的另一个优点就是:采用取样,而不是传统的代码植入技术,对应用性能的影响非常非常小,完全可以开着 JMC 来做压测(唯一影响可能是 full gc 多了)。
2.7.4 功能:实时监控 JVM 运行时的状态
如果是远程服务器,使用前要开 JMX。 -Dcom.sun.management.jmxremote.port={YOUR HOST/IP}
文件 -> 连接 -> 创建新连接, 填入上面 JMX 参数的 host 和 port
Mission Control的界面非常有特色,在默认的界面中,以飞机仪表的视图显示了Java堆使用率、CPU使用率和Live Set+Fragmentation。 Mission Control的一大特点是可以自由设置图标内容。比如,如果希望在飞机仪表面板再增加一个监控项,可以单击右侧的添加按钮“+”,按需添加各种统计图表。
- “触发器” tab 可以根据 CPU、线程等信息,设定一定的阈值,来触发报警。
- “内存” tab 提供 heap 和 GC 的信息。可以关注 GC次数、时间以及随着 GC 发生 heap 的内存变化情况,以此来调整 jvm 参数。
- “线程” tab 可以关注每条线程所占的CPU、死锁情况以及线程堆栈信息。
2.7.5 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连续收集低水平和详细的运行时信息进行高效,详细的分析。
2.7.5.1 事件类型
当启用时,JFR 将记录运行过程中发生的一系列事件。其中包括 Java 层面的事件,如线程事件、锁事件,以及 Java 虚拟机内部的事件,如新建对象、垃圾回收和即时编译事件。
按照发生时机以及持续时间来划分,JFR 的事件共有四种类型,它们分别为以下四种。
- 瞬时事件(Instant Event),用户关心的是它们发生与否,例如异常、线程启动事件。
- 持续事件(Duration Event),用户关心的是它们的持续时间,例如垃圾回收事件。
- 计时事件(Timed Event),是时长超出指定阈值的持续事件。
- 取样事件(Sample Event),是周期性取样的事件。
取样事件的其中一个常见例子便是方法抽样(Method Sampling),即每隔一段时间统计各个线程的栈轨迹。如果在这些抽样取得的栈轨迹中存在一个反复出现的方法,那么我们可以推测该方法是热点方法。
2.7.5.1 启动方式
- 方式1:使用-XX:StartFlightRecording=参数
- 方式2:使用jcmd的JFR.*子命令
- 方式3:JMC 的 JFR 插件
2.7.5.1 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等比较重要的信息展示。
2.7.5.1.1 代码
它可以显示系统中的热点方法和占用的时间,下图显示了占用CPU时间最多的方法调用树信息。
2.7.5.1.2 IO
在IO页面,还可以看到磁盘文件的读写情况,以及网络Socket的访问情况。
下图显示了在记录时间段内,程序通过Socket访问的远程主机以及数据读取次数和数据读取数量。
2.8 其它工具
2.8.1 Flame Graphs(火焰图)
在追求极致性能的场景下,了解你的程序运行过程中cpu在干什么很重要,火焰图就是一种非常直观的展示cpu在程序整个生命周期过程中时间分配的工具。
火焰图对于现代的程序员不应该陌生,这个工具可以非常直观的显示出调用栈中的CPU消耗瓶颈。
网上的关于java火焰图的讲解大部分来自于Brendan Gregg的博客:www.brendangregg.com/flamegraphs…
火焰图 ,简单通过x轴横条宽度来度量时间指标,y轴代表线程栈的层次。
2.8.2 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…
2.8.3 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动态调整目标应用程序的类以注入跟踪代码(“字节码跟踪”)。