作者丨黎杜


这一篇是JVM的调优实战篇,内容如下,包括常用的调优工具本地的和线上的,并且有案例结合使用来讲解,然后就是一些常见的实战场景,这个也是几乎大厂面试官会问的,大家面试的时候也可以直接拿来当案例:



java jvm调优 代码 jvm调优实战_java jvm调优 代码

调优工具

好了,首先我们来了解我们常用的调优工具吧,我这里推荐的GUI工具是:VisualVM,我不知道大家用的是哪一款。

VisualVM

VisualVM是jdk自带的调优工具,在安装的路径下jdk1.8.0_40\bin就可以看到jvisualvm工具了:


直接双击它就可以使用了,双击后打开的界面如下:



java jvm调优 代码 jvm调优实战_java_02

在打开的界面中,左边分为本地和远程,只要你本地有java进程启动,他就会自动检测到,比如 DeadLock(pid 8120) 就是我本地启动的死锁的案例。

你也可以连远程服务器的Java进程,不过一般正式环境的服务器都不会让你连接的,我们的正式环境只有QA才能操作,我们开发人员只能才做dev环境,像test环境都只能提测给QA有他们才能操作,这里我们就直接使用笨的操作。



java jvm调优 代码 jvm调优实战_java_03

在右边的内容区就可以看到我们主要关注的板块了,比如概述显示的就是我们启动Java进程时设置的JVM参数以及系统的属性(一般默认)



java jvm调优 代码 jvm调优实战_jvm_04

还有就是监视板块,它显示的GUI界面如上图所示,展示的是CPU的使用情况、堆大小的使用情况、Metadata(元空间的使用情况)、以及该进程中装载的类和线程数情况,所以可以通过这个界面至少可以观察到CPU的健康状况和内存堆大小的变化情况。

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



java jvm调优 代码 jvm调优实战_jvm_05

然后点击右边的线程Dump,就可以看到线程的堆栈信息:



java jvm调优 代码 jvm调优实战_java_06

可以看到两个线程在相互等待,各自等待的锁,都同时被对方持有就就陷入的死锁的局面。

所以通过第三板块的线程就能排查死锁的,然后就是抽样器和Profiler可以查看cpu的内存情况



java jvm调优 代码 jvm调优实战_java jvm调优 代码_07

最后的就是Visual GC,这个必须要安装插件才有,安装的方式就是在导航栏的工具下的插件就可以安装,具体安装的话可以百度一下,挺简单的:



java jvm调优 代码 jvm调优实战_大数据_08

在这块的内容就可以看到包括Compile Time(编译时间)、Class Loader Time(类加载器加载类的时间)、GCTime(总GC的时间)、Eden、S1、S0、Old Gen(老年代大小)、Matadata(元空间的大小)



java jvm调优 代码 jvm调优实战_java_09

对于调优Java堆,主要关注的几块内容就是下面这几块:Eden、S1、S0、Old Gen区域的大小变化,以及XXX collections、XXXms属性的变化,他们分别表示:发生了多少次GC,以及发生GC过程的时间是多少



java jvm调优 代码 jvm调优实战_大数据_10

还有一块是堆存储文件的分析,这一块也是在如下图所示,能够看到堆中实例数的占比,可以分析OOM问题、以及内存泄露的问题,结合线程的堆栈信息就能够及时的排查出OOM:

java jvm调优 代码 jvm调优实战_大数据_11

java jvm调优 代码 jvm调优实战_编程语言_12

关于Visual VM的详细使用就说到这里,基本开发中要用的,都提到了,详细的自己也可以找一找教程进行深入的学习。

Arthas

Arthas是阿里的开源项目之一,可以用于生产中分析Java进程的具体情况,分析的一些内容和Visual VM很多都是类似的,只不过人家是Linux环境的使用命令的方式。

下载

首先是下载Arthas,直接使用wget方式:

wget https://alibaba.github.io/arthas/arthas-boot.jar

下载后他就是一个jar项目,可以通过java命令直接启动。


java jvm调优 代码 jvm调优实战_java jvm调优 代码_13

对于后面的实例讲解,比如OOM案例死锁案例CPU飙高案例,为了节约时间,我都是直接使用javacjava命令来编译和运行的,比较方便,就不再创建SpringBoot项目了,大家也可以提前先配置好自己的jdk的环境变量。

启动

下载完Arthas后就可以启动它,直接通过下面命令进行启动:

java -jar arthas-boot.jar -h

我先启动自己的案例OOM


java jvm调优 代码 jvm调优实战_java_14

然后,再启动Arthas后,他就会自动检测到Java进程,然后选择你要监测的Java进程,比如OOM,所以直接输入1:


java jvm调优 代码 jvm调优实战_jvm_15

输入1后就成功进入到Arthas


java jvm调优 代码 jvm调优实战_jvm_16

下面来了解Arthas的常用的命令,非常详细的就不会讲解,太多命令了,只讲几个常用的,也是案例中用到的,详细的学习可以移步到这里:Arthas官方文档。

dashboard

输入dashboard 命令后就会出现监控面板:类图向界面,用于观察每个线程及所占的CPU


java jvm调优 代码 jvm调优实战_大数据_17

对于里面的每一个字段表示什么意思,应该英文不差的,也能知道,我这里直接查百度截个图,就不一个一个敲了,太多了,大家可以下面的这个图:


java jvm调优 代码 jvm调优实战_java jvm调优 代码_18

thread

thread命令是查看线程的信息的,包括线程堆栈信息,线程占用的CPU信息诸如此类。

thread的参数如下所示:


java jvm调优 代码 jvm调优实战_编程语言_19

直接输入thread后,就可以查看到其中的children-thread是占用cpu最高的,而children-thread这个名字是我创建这个线程时赋予这个现成的名字。


java jvm调优 代码 jvm调优实战_java jvm调优 代码_20

在阿里的开发手册中也有提到,对于线程池的创建最好命名,这样就是为了出现问题的时候方便排查原因:


java jvm调优 代码 jvm调优实战_java jvm调优 代码_21

阿里这个开发手册挺棒的,强推一下,它的开发手册除了一些规范之外,也包括一些调优:sql调优,有兴趣的可以看一看,之前应该挺多技术博主都在公众号中发过这本pdf。

jvm

jvm这个命令就可以用于查看当前JVM信息


java jvm调优 代码 jvm调优实战_编程语言_22

上图详细的字段信息可以参考以下图所示:


java jvm调优 代码 jvm调优实战_java jvm调优 代码_23

trace

trace命令是用来监测类内部方法的调用的,以及方法调用的耗时情况


java jvm调优 代码 jvm调优实战_编程语言_24

如果一个方法的耗时过长,那么这个方法很可能就有问题,需要review原来的代码。


java jvm调优 代码 jvm调优实战_jvm_25

sc -d 类名还可以查看类的信息,如下所示:


java jvm调优 代码 jvm调优实战_java_26

jad

jad命令是将JVM中实际运行的class的byte code反编译成 java 代码,是比较强大的命令,比如:我们可能要线上检查代码的一些问题,就可以使用这个命令进行反编译:


java jvm调优 代码 jvm调优实战_jvm_27

watch

watch命令可以用来查看制定方法的调用情况,观察的范围包括:返回值、抛出异常、入参等。


java jvm调优 代码 jvm调优实战_大数据_28

具体的详细参数可以参考如下:


java jvm调优 代码 jvm调优实战_大数据_29

原生命令

除了上面介绍的第三方的工具,还有可以使用linux的原始命令来排查问题,主要有以下几种命令:

  • jps
  • top
  • jstack
  • jstat
  • jmap
  • jhat
  • jinfo
jps

jps可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier)。


java jvm调优 代码 jvm调优实战_java_30

这里查到Java进程的Id后就可以进行后面的操作,比如查进程内的线程情况。

若是想详细了解jps命令的的一些参数,可以使用man命令进行查询,比如man jps


java jvm调优 代码 jvm调优实战_java jvm调优 代码_31

选项

作用

-q

用于只输出进程PID信息,省略主类的名称信息

-m

输出虚拟机启动的时候传给主类main()函数的参数

-l

输出主类的全名,以及包名的全路径

-v

输出虚拟机进程启动时的JVM参数

top

top命令是查询各个进程的cpu使用情况,可以结合ps来使用,如下图所示:

java jvm调优 代码 jvm调优实战_java jvm调优 代码_32

java jvm调优 代码 jvm调优实战_编程语言_33

jstack

jstack主要是用于查询线程的堆栈信息情况,用它就可以轻而易举的排查死锁的信息,比如如下图所示就是排查死锁,使用jstack pid,若是发现死锁,它会给你提示的:

java jvm调优 代码 jvm调优实战_java jvm调优 代码_34

java jvm调优 代码 jvm调优实战_编程语言_35

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的一些情况


java jvm调优 代码 jvm调优实战_编程语言_36

上面的字段分别表示为:

列名

作用

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来查询。

java jvm调优 代码 jvm调优实战_大数据_37

java jvm调优 代码 jvm调优实战_大数据_38

好了,这一篇限于篇幅的原因,先写到这里,下一篇再继续了解JVM的调优实战案例分析,原来想一篇写完的,但是篇幅估计得达到两万多字,太长了,所以分两篇吧

-End-