JVM实战调优篇(一)

1,203 阅读11分钟

关于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的健康状况和内存堆大小的变化情况。

第三个板块线程肯定就是现实线程的运行时的信息,可以看到这里直接就显示检测到死锁的存在,可以说这个工具还是非常的智能的,在所有线程中可以看看到两个爆红的DeadLock1DeadLock2线程。

然后点击右边的线程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飙高案例,为了节约时间,我都是直接使用javacjava命令来编译和运行的,比较方便,就不再创建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(伊甸园)目前已使用空间 (字节)
OCOld代的容量 (字节)
OUOld代目前已使用空间 (字节)
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获取,这一期就到这里,我是黎杜,我们下一期见。