JVM性能调优(4)——性能调优工具

JVM性能调优(4)——性能调优工具

目录


前序文章:

JVM性能调优(1) —— JVM内存模型和类加载运行机制

JVM性能调优(2) —— 垃圾回收器和回收策略

JVM性能调优(3) —— 内存分配和垃圾回收调优

回到顶部

一、JDK工具

先来看看有哪些常用的工具可以辅助我们进行性能调优和问题排查,后面再通过一个具体的示例结合工具来分析调优。

1、JDK工具

JDK自带了很多性能监控工具,我们可以用这些工具来监测系统和排查内存性能问题。



2、利用 jps 找出进程

jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令,简单实用,非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。

1)查看Java进程PID

【jps -l】左边一列就是Java进程的PID。



2)输出传递给JVM的参数

【jps -vl】



3、利用 jstat 查看VM统计信息

使用 jstat 工具可以监测 Java 应用程序的实时运行情况,可以看到VM内的Eden、Survivor、老年代的内存使用情况,还有 YoungGC 和 FullGC 的执行次数以及耗时。通过这些指标,我们可以轻松的分析出当前系统的运行情况,判断当前系统的内存使用压力以及GC压力,还有内存分配是否合理。

1)查看 jstat 有哪些操作

【jstat -options】



其中 jstat -gc 是最完整、最常用、最实用的命令,基本足够分析jvm的运行情况了。

2)显示 ClassLoad 的相关信息

【jstat -class <pid>】



3)查看内存使用和GC情况

【jstat -gc <pid> [<interval> [<count>]】



4)查看垃圾回收统计

【jstat -gcutil <pid> [<interval> [<count>]】



4、利用 jmap 查看对象分布情况

使用 jmap 可查看堆内存初始化配置信息以及堆内存的使用情况,输出堆内存中的对象信息,包括产生了哪些对象,对象数量多少等。

1)查看堆内存情况

【jmap -heap <PID>】

这个命令会打印出堆内存相关的一些参数设置以及各个区域的情况,要查看这些信息一般使用 jstat 命令就足够了。



2)查看系统运行时对象分布

【jmap -histo[:live] <PID>】带上 live 则只统计活对象

这个命令会按照各种对象占用内存空间的大小降序排列,把占用内存最多的对象放在最上面。通过这个命令可以简单的了解下当前jvm中的对象对内存占用的情况以及当前内存里到底是哪个对象占用了大量的内存空间。



3)生成堆内存转储快照

【jmap -dump:format=b,file=<path> <pid>】

【jmap -dump:live,format=b,file=<path> <pid>】

jmap -dump 是输出堆中所有对象;jmap -dump:live 是输出堆中所有活着的对象,而且 jmap -dump:live 会触发 FullGC,线上使用要注意。format=b 是以二进制格式输出;file 是文件路径,格式为 hrpof 后缀。

这个命令会在当前目录下生成一个 dump.hrpof 文件,这是个二进制的格式,无法直接打开,可以使用MAT等工具来分析。这个命令把这一时刻VM堆内存里所有对象的快照放到文件里去了,供你后续去分析。



5、利用 jstack 分析线程栈

jstack 是一种线程堆栈分析工具,最常用的功能就是使用 jstack pid 命令查看线程的堆栈信息,通常会结合 top -Hp pid 或 pidstat -p pid -t 一起查看具体线程的状态,也经常用来排查一些死锁的异常、CPU占用高的线程等。

1)jstack参数

2)查看线程堆栈信息

【jstack <pid> > stack.log】

这个命令可以把程序的线程堆栈dump下来。每个线程堆栈的信息中,都可以查看到线程 ID、线程状态(wait、sleep、running 等状态)以及是否持有锁等。



回到顶部

二、Linux 命令行工具

1、top 命令

top 命令是我们在 Linux 下最常用的命令之一,它可以实时显示正在执行进程的 CPU 使用率、内存使用率以及系统负载等信息。其中上半部分显示的是系统的统计信息,下半部分显示的是进程的使用率统计信息。



看第一行:主要展示了CPU的负载情况

最重要的就是看 load average,比如机器是4核CPU,那么 0.19、0.27、0.30,说明4核中连一个核都没用满,4核CPU基本很空闲。如果CPU负载是1,说明有1个核被使用的比较繁忙了。如果负载是4,说明4核CPU都跑满了;如果超过4,说明4核CPU被繁忙的使用还不够处理当前的任务,很多进程可能一直在等待CPU去执行自己的任务。

② 查看具体线程使用系统资源情况



2、vmstat 命令

vmstat 是 Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存、进程、CPU活动进行监控。

命令格式:【vmstat [ 选项 ] [ <时间间隔> ] [ <次数> ]】



字段说明:

判断指标:

3、pidstat 命令

如果是监视某个应用的上下文切换,可以使用 pidstat 命令监控指定进程的上下文切换。

pidstat 是 Sysstat 中的一个组件,也是一款功能强大的性能监测工具,我们可以通过命令:yum install sysstat 安装该监控组件。top 和 vmstat 两个命令都是监测进程的内存、CPU 以及 I/O 使用情况,而 pidstat 命令则是深入到线程级别。

命令格式:【pidstat [ 选项 ] [ <时间间隔> ] [ <次数> ]】

1)常用的选项:

2)查看所有进程的 CPU 使用情况

【pidstat】、【pidstat -u -p ALL】



3)显示每个进程的上下文切换情况

【pidstat -w -p <PID> <时间间隔> <次数>】



4)显示进程中线程的统计信息

【pidstat -p <PID> -t】



回到顶部

三、可视化工具

下面简单介绍几款常用的可视化分析工具,一般我们需要将GC日志文件、堆转储文件dump下来,然后就可以通过这些工具来分析。如果是线上分析一般直接使用上面的那些JDK命令行工具就足够了,这些可视化工具可以做一些辅助性的分析。

1、jvisualvm — JVM监控

jvisualvm 是 jdk 提供的监控工具,位于
%JAVA_HOME%/bin/jvisualvm.exe,双击运行即可。

VisualVM 提供了一个可视界面,用于查看JVM上运行的基于 Java 技术的应用程序的详细信息。VisualVM 能够监控线程,内存情况,方法的CPU时间和内存中的对象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)等。

1)插件安装

VisualVM 基于NetBeans平台开发工具,它具备通过插件扩展功能的能力,有了插件扩展支持,VisualVM可以做到:

可以从工具选项中打开插件面板安装所需的插件:



2)监视面板

在左边选择需要监控的程序,右边就可以可查看CPU、堆、线程等波动情况,也可以直接在这里进行手动 GC 和堆 Dump 操作。



3)线程面板

可看到所有的线程,以及线程的运行状态。点击面板的线程 Dump 按钮,可以查看线程瞬时的线程栈。(通过 jstack 也可以抓取线程栈)



4)GC面板

可以很方便的看到GC趋势图。(也可使用 jstat 工具监控)



5)分析堆转储快照

通过左上角装入快照按钮打开 dump 的堆转储文件,就可以分析堆转储快照了。





3、GCViewer — 离线分析GC日志



4、GCeasy — 在线分析GC日志

GCeasy 是一款在线版的非常直观的 GC 日志分析工具,我们可以将日志文件压缩之后,上传到 GCeasy 官网即可看到非常清楚的 GC 日志分析结果。





5、FastThread — 分析线程栈

线程栈使用 jstack 命令 dump 下来后,可以使用 FastThread 在线工具分析线程栈信息,可以直观的看到有多少线程、线程池、线程的状态、是否有死锁等信息。



6、MAT — 分析堆转储文件

我们使用 jmap 命令 dump 下来的堆转储文件可以使用 MAT 来分析,可以分析创建了哪些对象,然后分析对象的引用链,找出问题所在。MAT 下载地址:
eclipse.org/mat/downloa

MAT 主要功能:



7、性能调优工具

调优工具选择

调优的工具很多,一般对于线上系统,使用 jstat 工具足以分析出JVM的运行情况,如果有GC日志,也可以使用GCeasy快速分析JVM的运行情况。

遇到CPU负载过高,可以使用 top + pidstat 找出负载高的线程,或者直接使用 jstat 观察是不是在频繁FullGC。

遇到死锁、OOM等问题,可以用 jstack 把线程栈dump下来分析,还可以结合 FastThread 在线工具,分析线程栈、哪些线程阻塞等待等。

遇到OOM问题,使用 jmap 把堆转储快照dump下来,用MAT来分析创建了哪些大量的对象。

8、JVM监控平台

系统上线后,如果不部署可视化的监控平台,我们一般就通过上面的这些工具来分析JVM的内存运转模型、GC情况等,可以在机器上运行jstat,让其把监控信息写入一个文件,每天定时检查—下。

监控平台则可以通过可视化的界面看到JVM各个区域的内存变化,GC次数和GC耗时等信息,以及出现性能问题时能及时报警。

一般可以部署 Zabbix、Ganglia、Open-Falcon、Prometheus 之类的可视化监控平台,把线上系统接入到这些平台,就可以直接图形化看到JVM的表现。

回到顶部

四、利用工具分析JVM运行情况

要想合理地分配内存、优化GC,通过前一篇的性能调优过程可以发现,我们至少需要知道如下的一些信息:新生代对象增长的速率,YoungGC的触发频率,YoungGC的耗时,每次YoungGC后存活对象大小,每次YoungGC过后有多少对象进入了老年代,老年代对象增长的速率,FullGC的触发频率,FullGC的耗时等。前面我们是通过分析GC日志或者粗略估算的方式来调优的,现在就利用 jstat 工具来分析下。

1、运行示例程序

1)如下示例代码

这段代码模拟每秒钟在新生代创建20M对象,1秒之后就变为垃圾对象了。





2)设置JVM参数

运行示例程序前设置如下的JVM参数:新生代、老年代各100M,Eden区80M,Survivor区10M,大对象阀值20M。





3)估算内存运转模型

我们先根据这段业务代码以及JVM参数配置估算下JVM运行情况:

4)使用 jps 命令找出程序的 PID

将程序运行起来,首先通过 jps -l 命令找到这个程序的PID。



5)使用 jstat 命令查看GC情况

如下是 jstat 输出的情况:



View Code

首先从前面几行可以看出内存各个区域的大小,Survivor0/Survivor1 10M,Eden区80M,老年代100M 等信息。



2、新生代对象增长的速率

从 EU 这一行可以看出,新生代基本是按照每秒20M左右的对象在增长。



3、YoungGC的触发频率和耗时

从 Eden 区的内存变化可以看出,基本是每隔3秒或4秒就会触发一次 YoungGC,比如第一次Eden区增长到66424.2时,经过一次 YoungGC后只剩下11878.4。从 YGC 这列也大致可以看出YoungGC的频率。

从 YGCT 这列可以看出,每次YoungGC耗时1~5毫秒的样子,也就是说每隔3~4秒,触发一次YoungGC,一次YoungGC系统卡顿1~5毫秒。这也可以看出 YoungGC 其实是很快的,就算新生代800M也才10几毫秒,对系统几乎没什么影响。



4、YoungGC后存活对象大小以及有多少对象进入了老年代

从 S0U、S1U 这两列的变化可以看出,每次YoungGC后有800K左右的对象进入 Survivor 区。

从 OU 这列的变化可以看出,每次进入老年代的对象在10M左右,所以一次YoungGC后可能有10M的存活对象进入老年代。



5、FullGC的触发频率和耗时

从 OU 这列的变化可以看出,在老年代达到 82573.2 时,触发了 FullGC,回收后老年代大小为 10892.1。从整体的时间线上看,刚好60秒就触发了一次FullGC。

从 FGCT 可以看出,一次FullGC 耗时2毫秒,



为什么在老年代 82573.2 时就触发了FullGC呢,我们从GC日志中来看:



可以看出这一秒内,实际上这次YoungGC导致有10M的对象进入老年代,老年代实际有92815K对象,因而应该是CMS超过了 92% 的阀值之后触发了老年代GC。

6、使用GCeasy查看GC日志

至此,其实已经基本上分析出整个JVM的运转情况了。这里总结下:

从上面的分析可以看出,jstat 监控的输出结果基本是符合前面估算的结果的。但是粗略估算需要熟悉系统核心的业务,而且其它未知因素也比较多,粗略估算一般用于系统刚上线阶段来设置JVM参数。而通过 jstat 来监控一般就可以比较准确的摸清JVM的运行情况,然后进行性能调优。

接下来再通过GC日志来看下是否符合分析的情况,GC日志就不再一行一行分析了,我们直接通过在线工具 GCeasy 来看看内存变化和GC的情况。将输出的GC日志直接拷贝到 GCeasy 上,就可以看到分析的结果。

1)GC总体情况

从这张图可以得到如下信息:



2)YoungGC频率

从 Young Gen 这个统计图可以看出,YoungGC的频率在3~4秒的样子。



3)老年代GC频率

从这张统计图可以看出,老年代是每两次YoungGC增长一次,每次增长10M左右,在60秒左右触发一次OldGC。



4)CMS回收情况

这张图展示了CMS各个阶段的统计情况



7、性能优化

从上面的分析可以看出,这个JVM最大的问题在于 Survivor 区没起作用,Survivor 区只有10M,而YoungGC后存活对象大于10M,导致无法放进Survivor区而直接进入老年代了,进而触发FullGC。因此我们可以增大新生代大小或者调整Eden区和Survivor区比例,来让对象进入Survivor区。

比如改为如下配置:新生代给150M,比例调整为6,这样Eden区90M,Survivor区各30M,这样Survivor区足以放下YoungGC后存活的对象,也基本上能避免动态年龄判断导致对象进入老年代。

再看这时的GC情况,首先从 S0U、S1U 的变化可以看出,Survivor 区在起作用了,每次YoungGC后存活对象都进入Survivor区了。

然后从 OU、YGC的变化可以看出,有部分长期存活的对象在YoungGC次数超过设置的GC年龄阀值(设置的5岁)后,进入了老年代。

从 FGC 这列可以看出,Survivor 区合理设置后,再没有发生过 FullGC 了。



回到顶部

五、使用 MAT 分析OOM问题

对于排查 OOM 问题、分析程序堆内存使用情况,最好的方式就是分析堆转储,堆转储,包含了堆现场全貌和线程栈信息。这节就来看看如何使用MAT分析OOM问题。

1、运行示例程序

准备如下两个测试类:

设置如下JVM参数:

运行程序后报OOM异常:



2、MAT 分析OOM问题的思路

对于线上运行的程序,如果我们不能通过日志快速定位出OOM的根源,一般就可以使用MAT来分析OOM的问题。

使用 MAT 分析 OOM 问题,一般可以按照以下思路进行:

如果dump出来的内存快照很大,比如有几个G,务必在启动MAT之前,先在配置文件(MemoryAnalyzer.ini)里给MAT本身设置—下堆内存大小(默认为1024m),比如设置为4个G,或者8个G。

3、总览图 — 快速分析OOM问题

使用MAT打开堆转储文件 dump.hprof,打开后先进入的是概览信息界面:

从饼图可以看出,明显有对象占用了大量内存,然后再看 Problem Suspect1,已经说明了 main 线程通过局部变量占据了 99.42% 内存的对象,而且是 java.lang.Object[] 数组占据了大量内存。



点击 Details 进去查看详细的说明,从 “Accumulated Objects in Dominator Tree” 支配树可以看出,main 线程引用了 OomService 对象,OomService 引用了一个 ArrayList 对象,然后 ArrayList 存储了大量 String 对象。这里基本上就能分析出OOM的根源了。



再点击 See stacktrace 看看线程栈基本就能定位到问题代码了。



4、直方图 — 定位根源

工具栏的第二个按钮可以打开直方图,直方图按照类型进行分组,列出了每个类有多少个实例,以及占用的内存。

可以看到,char[] 字节数组占用内存最多,对象数量也很多,第二位的 String 对象数量也非常多,有 9791 个,从这大概可以猜出应该是创建了大量的 String 对象。



在 char[] 上点击右键,选择 List objects -> with incoming references,就可以列出所有的 char[] 实例,以及每个 char[] 的整个引用关系链:



随机展开一个 char[],如下图所示:

右侧框中可以看到整个引用链,左侧的框可以查看每一个实例的内部属性。

通过这个引用链可以发现是 String 对象引用了 char[] 数组(String 的内部结构就是一个 char[] 数组),说明创建了大量的 String 对象;然后 String 对象又被 ArrayList 的 Object[] 数组引用着,说明是大量 String 对象放入了 ArrayList 中,然后这个 ArrayList 又被 OomService 的 data 变量引用着。到这里就定位出了引发OOM的类了。

Retained Heap(深堆)代表对象本身和对象关联的对象占用的内存,Shallow Heap(浅堆)代表对象本身占用的内存。比如,OomService 中的 data 这个 ArrayList 对象本身只有 16 字节,但是其所有关联的对象占用了 130+MB 内存。



如果希望看到完整内容的话,可以右键选择 Copy->Value,把值复制到剪贴板或保存到文件中:



5、支配树 — 定位根源

其实,使用直方图定位 OomService,已经走了些弯路。可以点击工具栏中第三个按钮进入支配树界面。这个界面会按照对象保留的 Retained Heap 倒序直接列出占用内存最大的对象。

可以看到,第一位就是 OomService,整个路径是 OomSerice -> ArrayList -> Object[] -> String -> char[] 。



6、线程栈 — 分析代码

可以点击工具栏的第五个按钮,打开线程视图来分析 OomService 执行什么逻辑。可以看到 OomService 是 OomMain 的一个本地变量,然后 OomMain 调用了 OomService 的 addData 方法,然后 addData 方法里应该是通过 Stream 生成一个字符串放入 data 中的。



7、OQL—查询数据

点击工具栏的第四个按钮,来到 OQL 界面。在这个界面,我们可以使用类似 SQL 的语法,在 dump 中搜索数据。可以看到只创建了一个 OomService 实例,说明只有一个地方调用了 OomService 的方法。



然后可通过 List objects 功能搜索引用OomService 的对象:



可以看到其被 main 线程引用着:



回到顶部

六、使用 Arthas 分析高 CPU 问题

Arthas 是阿里开源的 Java 诊断工具,相比 JDK 内置的诊断工具,要更人性化,并且功能强大,可以实现许多问题的一键定位,而且可以一键反编译类查看源码,甚至是直接进行生产代码热修复,实现在一个工具内快速定位和修复问题的一站式服务。

Arthas 官方文档:
alibaba.github.io/artha

1、运行示例程序

准备如下导致CPU负载高的代码:代码中创建了2个线程的线程池,提交的任务通过 BCryptPasswordEncoder 对一个长字符串加密,这个是非常消耗CPU的。

2、启动 Arthas

启动后,直接找到我们要排查的 JVM 进程,然后可以看到 Arthas 附加进程成功:



输入 help 命令,可以看到所有支持的命令列表。这里主要会用到 dashboard、thread、jad、watch 等命令,来定位高CPU的问题。



3、dashboard — 展示整体情况

dashboard 命令整体展示了进程所有线程、内存、GC 等情况,可以明显看到两个CPU占用很高的线程,从线程名字来看应该是线程池的线程。



4、thread — 查看高CPU的线程

接下来,查看最繁忙的线程在执行的线程栈,可以使用 thread -n 命令。这里,我们查看下最忙的 2 个线程:从线程栈可以看出,应该就是 CpuService 的 randomEncode 方法调用 BCryptPasswordEncoder 的 encode 方法导致CPU负载高的。



5、watch — 监控参数

如果想要观察方法的入参和出参,可以用 watch 命令来观察:



6、jad — 反编译

前面已经分析出CPU负载高的位置是 CpuService 的 randomEncode 了,那么通过 jad 反编译来看看源码长什么样子,方便我们进一步定位问题。



7、redefine — 重载类

如果我们想做线上调试,又不想在本地改代码,打印日志,再提交到服务器,再重启服务测试,那我们可以结合 arthas 的 jad、mc、redefine 来动态重定义类。

① 首先用 jad 把源文件下载下来



然后修改下源码:添加了一行输出日志



② 使用 mc 命令反编译源文件

反编译后会生成对应的 class 文件:



③ 使用 redefine 重载类



就可以看到控制台已经在输出我们打印的日志了:



需要额外说明的是,由于 monitor、trace、watch 等命令是通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此诊断结束要执行 shutdown 来还原类或方法字节码,然后退出 Arthas。

学习更多JAVA知识与技巧,关注与私信博主(学习)


深圳SEO优化公司湛江网站推广方案公司绍兴网站设计报价上饶网站建设设计多少钱济南关键词按天计费公司大浪SEO按天计费哪家好榆林seo排名价格益阳关键词按天收费公司辽源营销网站报价常州网站优化按天收费哪家好松岗网页制作多少钱苏州网络推广报价毕节百度标王辽源seo优化价格吕梁SEO按天扣费推荐广东网站搭建推荐九江优化公司桐城百姓网标王推广公司徐州模板推广多少钱石家庄关键词按天计费推荐贵阳百度标王报价东营百度关键词包年推广推荐迁安模板制作多少钱呼和浩特企业网站改版报价张家界关键词按天收费哪家好濮阳网站优化排名公司宁波百度网站优化排名茂名至尊标王哪家好塘坑网站设计模板哪家好石岩网站推广方案价格中卫优化报价歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化