JVM性能调优实战篇(一)
关于JVM的的第三篇文章来了,这一篇是JVM的调优实战篇,上一篇是调优实战的基础理论篇,主要是关于GC方面的,因为对于JVM的调优基本就是Java堆了,所以涉及到的区域几乎与GC是脱不了关系的。
这一篇主要的内容如下,包括常用的调优工具本地的和线上的,并且有案例结合使用来讲解,然后就是一些常见的实战场景,这个也是几乎大厂面试官会问的,大家面试的时候也可以直接拿来当案例:
调优工具
好了,首先我们来了解我们常用的调优工具吧,我这里推荐的GUI工具是:VisualVM,我不知道大家用的是哪一款。
VisualVM
VisualVM是jdk自带的调优工具,在安装的路径下jdk1.8.0_40\bin就可以看到jvisualvm工具了:
直接双击它就可以使用了,双击后打开的界面如下:
在打开的界面中,左边分为本地和远程,只要你本地有java进程启动,他就会自动检测到,比如 DeadLock(pid 8120) 就是我本地启动的死锁的案例。
你也可以连远程服务器的Java进程,不过一般正式环境的服务器都不会让你连接的,我们的正式环境只有QA才能操作,我们开发人员只能才做dev环境,像test环境都只能提测给QA有他们才能操作,这里我们就直接使用笨的操作。
在右边的内容区就可以看到我们主要关注的板块了,比如概述显示的就是我们启动Java进程时设置的JVM参数以及系统的属性(一般默认)。
还有就是监视板块,它显示的GUI界面如上图所示,展示的是CPU的使用情况、堆大小的使用情况、Metadata(元空间的使用情况)、以及该进程中装载的类和线程数情况,所以可以通过这个界面至少可以观察到CPU的健康状况和内存堆大小的变化情况。
第三个板块线程肯定就是现实线程的运行时的信息,可以看到这里直接就显示检测到死锁的存在,可以说这个工具还是非常的智能的,在所有线程中可以看看到两个爆红的DeadLock1和DeadLock2线程。
然后点击右边的线程Dump,就可以看到线程的堆栈信息:
可以看到两个线程在相互等待,各自等待的锁,都同时被对方持有就就陷入的死锁的局面。
所以通过第三板块的线程就能排查死锁的,然后就是抽样器和Profiler可以查看cpu的内存情况:
最后的就是Visual GC,这个必须要安装插件才有,安装的方式就是在导航栏的工具下的插件就可以安装,具体安装的话可以百度一下,挺简单的:
在这块的内容就可以看到包括Compile Time(编译时间)、Class Loader Time(类加载器加载类的时间)、GCTime(总GC的时间)、Eden、S1、S0、Old Gen(老年代大小)、Matadata(元空间的大小)。
对于调优Java堆,主要关注的几块内容就是下面这几块:Eden、S1、S0、Old Gen区域的大小变化,以及XXX collections、XXXms属性的变化,他们分别表示:发生了多少次GC,以及发生GC过程的时间是多少。
还有一块是堆存储文件的分析,这一块也是在如下图所示,能够看到堆中实例数的占比,可以分析OOM问题、以及内存泄露的问题,结合线程的堆栈信息就能够及时的排查出OOM:
关于Visual VM的详细使用就说到这里,基本开发中要用的,都提到了,详细的自己也可以找一找教程进行深入的学习。
Arthas
Arthas是阿里的开源项目之一,可以用于生产中分析Java进程的具体情况,分析的一些内容和Visual VM很多都是类似的,只不过人家是Linux环境的使用命令的方式。
下载
首先是下载Arthas,直接使用wget方式:
wget https://alibaba.github.io/arthas/arthas-boot.jar
下载后他就是一个jar项目,可以通过java命令直接启动。
对于后面的实例讲解,比如OOM案例,死锁案例,CPU飙高案例,为了节约时间,我都是直接使用javac和java命令来编译和运行的,比较方便,就不再创建SpringBoot项目了,大家也可以提前先配置好自己的jdk的环境变量。
启动
下载完Arthas后就可以启动它,直接通过下面命令进行启动:
java -jar arthas-boot.jar -h
我先启动自己的案例OOM
然后,再启动Arthas后,他就会自动检测到Java进程,然后选择你要监测的Java进程,比如OOM,所以直接输入1:
输入1后就成功进入到Arthas:
下面来了解Arthas的常用的命令,非常详细的就不会讲解,太多命令了,只讲几个常用的,也是案例中用到的,详细的学习可以移步到这里:Arthas官方文档。
dashboard
输入dashboard 命令后就会出现监控面板:类图向界面,用于观察每个线程及所占的CPU。
对于里面的每一个字段表示什么意思,应该英文不差的,也能知道,我这里直接查百度截个图,就不一个一个敲了,太多了,大家可以下面的这个图:
thread
thread命令是查看线程的信息的,包括线程堆栈信息,线程占用的CPU信息诸如此类。
thread的参数如下所示:
直接输入thread后,就可以查看到其中的children-thread是占用cpu最高的,而children-thread这个名字是我创建这个线程时赋予这个现成的名字。
在阿里的开发手册中也有提到,对于线程池的创建最好命名,这样就是为了出现问题的时候方便排查原因:
阿里这个开发手册挺棒的,强推一下,它的开发手册除了一些规范之外,也包括一些调优:sql调优,有兴趣的可以看一看,之前应该挺多技术博主都在公众号中发过这本pdf。
jvm
jvm这个命令就可以用于查看当前JVM信息
上图详细的字段信息可以参考以下图所示:
trace
trace命令是用来监测类内部方法的调用的,以及方法调用的耗时情况:
如果一个方法的耗时过长,那么这个方法很可能就有问题,需要review原来的代码。
sc -d 类名还可以查看类的信息,如下所示:
jad
jad命令是将JVM中实际运行的class的byte code反编译成 java 代码,是比较强大的命令,比如:我们可能要线上检查代码的一些问题,就可以使用这个命令进行反编译:
watch
watch命令可以用来查看制定方法的调用情况,观察的范围包括:返回值、抛出异常、入参等。
具体的详细参数可以参考如下:
原生命令
除了上面介绍的第三方的工具,还有可以使用linux的原始命令来排查问题,主要有以下几种命令:
jps top jstack jstat jmap jhat jinfo
jps
jps可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier)。
这里查到Java进程的Id后就可以进行后面的操作,比如查进程内的线程情况。
若是想详细了解jps命令的的一些参数,可以使用man命令进行查询,比如man jps:
选项 | 作用 |
---|---|
-q | 用于只输出进程PID信息,省略主类的名称信息 |
-m | 输出虚拟机启动的时候传给主类main()函数的参数 |
-l | 输出主类的全名,以及包名的全路径 |
-v | 输出虚拟机进程启动时的JVM参数 |
top
top命令是查询各个进程的cpu使用情况,可以结合ps来使用,如下图所示:
jstack
jstack主要是用于查询线程的堆栈信息情况,用它就可以轻而易举的排查死锁的信息,比如如下图所示就是排查死锁,使用jstack pid,若是发现死锁,它会给你提示的:
jstat
jstat是虚拟机统计信息的监视工具,用于统计虚拟机运行时状态信息的命令,比如GC信息、内存信息、即时编译器运行时的信息等。
jstat有挺多参数的,常用的如下所示:
选项 | 用途 |
---|---|
-class | 输出类加载、卸载数量、总空间以及类卸载耗时 |
-gc | 输出Java堆情况,包括Eden、S1、S0、老年代、等空间的容量、已使用量、垃圾回收时间等信息 |
-gccapaccity | 也是监视Java堆的情况、但是主要关注Java堆各个区域使用到最大、最小空间 |
-gcutil | 输出Java堆中已使用空间占用总空间的百分比 |
-gccause | 与-gcutil功能一样,会额外输出上一次垃圾收集产生的原因 |
-gcnew | 输出新生代的情况 |
-gcnewcapacity | 也只关注新生代,主要是关注使用的最大、最小空间 |
-gcold | 输出老年代的情况 |
-gcoldcapacity | 也只关注老年代,主要是关注使用的最大、最小空间 |
-gcpermcapacity | 输出永久代使用的最大、最小空间 |
-compiler | 输出即时编译过的方法、耗时等信息 |
-printcomilation | 输出已经被编译过的方法 |
使用jstat -gc pid就可以查看如下图所示的gc的一些情况
上面的字段分别表示为:
列名 | 作用 |
---|---|
S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
S0U | 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) |
S1U | 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) |
EC | 年轻代中Eden(伊甸园)的容量 (字节) |
EU | 年轻代中Eden(伊甸园)目前已使用空间 (字节) |
OC | Old代的容量 (字节) |
OU | Old代目前已使用空间 (字节) |
MC | 元空间的容量 (字节) |
MU | 元空间目前已使用空间 (字节) |
YGC | 从应用程序启动到采样时年轻代中gc次数 |
YGCT | 从应用程序启动到采样时年轻代中gc所用时间(s) |
FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
GCT | 从应用程序启动到采样时gc用的总时间(s) |
关于jstat的一些其他参数的详细解释,大家可以查查文档或者直接使用man jstat来查看。
jmap
jmap应该是使用比较少的工具,因为他的作用就是生成堆转储快照,但是它会消耗系统资源,所以一般不用,取而代之的就是使用JVM参数:-XX:+HeapDumpOnOutOfMemoryError和 -XX:HeapDumpPath= ${目录} 。
然后,就是使用内存分析工具堆堆转储文件进行分析一波,所以这个命令,大家了解一下就好,不过一般项目比较小的话,一般的传统项目,你还是可以用的,对于互联网项目那就不行了。
jhat
jhat也是大家要了解就行的命令,它主要是虚拟机堆转储快照分析工具,因为一般都会生成堆转储文件,然后使用GUI界面来分析,很少在线上分析堆转储文件。
因为他也是要消耗服务器性能的,而且一般分析的过程都会比较长,所以不会采用在线上分析的方法。
jinfo
jinfo的作用是实施的查看和调整虚拟机的各项参数,不过我们可能一般更加关注的是在虚拟机启动的时候,我们设置的参数,可以通过jps -v来查询。
好了,这一篇限于篇幅的原因,先写到这里,下一篇再继续了解JVM的调优实战案例分析,原来想一篇写完的,但是篇幅估计得达到两万多字,太长了,所以分两篇吧,有需要阿里巴巴开发手册的,和文章中使用到的案例的源代码的,可以加我微信:abc730500468获取,这一期就到这里,我是黎杜,我们下一期见。
你点的每个赞,我当做都认真