JVM(九)性能监控与调优

背景说明

为什么要调优

  • 防止出现OOM
  • 解决OOM
  • 减少Full GC出现的频率

监控的依据

  • 运行日志
  • 异常的堆栈信息
  • GC日志
  • 线程快照
  • 堆转储快照

性能优化步骤

性能监控(发现问题)

一种以非强行或者入侵凡是收集或查看应用运行性能数据的活动。监控通常是指一种在生产、质量评估或者开发环境下实时的带有预防或主动性的活动。当应用相关干系人提出性能问题却没有提供足够多的线索是,首先我们需要进行性能监控,随后是性能分析。

  • GC频繁
  • CPU load 过高
  • OOM
  • 内存泄露
  • 死锁
  • 程序响应时间过长

性能分析(排查问题)

一种以侵入方式收集运行性能数据的活动,它会影响应用的吞吐量或响应性。性能分析是针对性能问题的答复结果,关注的范围通常比性能监控更加集中。性能分析很少在生产环境下进行,通常是在质量评估、系统测试或者开发环境下进行,是性能监控之后的步骤。

  • 打印GC日志,通过GCViewer或者GCeasy来分析日志信息
  • 灵活运用命令行工具,jstack,jmap,jinfo等
  • dump出推文件,使用内存分析工具分析文件
  • 使用阿里Arthas,或jconsole,JVisualVM来实时查看JVM状态
  • jstack查看堆栈信息

性能调优(解决问题)

一种为改善应用响应性或吞吐量而更改参数、源代码、属性配置的活动,性能调优是在性能监控、性能分析之后的活动。

  • 适当增加内存,根据业务背景选择垃圾回收器
  • 优化代码,控制内存使用
  • 增加机器,分散节点压力
  • 合理设置线程池线程数量
  • 使用中间件提高程序效率,比如缓存、消息队列等。
  • 其它…

性能评价/测试指标

  • 停顿时间(响应时间):①提交请求和返回请求的响应之间使用的时间,一般比较关注平均响应时间。
    在这里插入图片描述
    ②在垃圾回收环节中:暂停时间是执行垃圾收集时,程序的工作线程被暂停的时间。-XX:MaxGCPauseMillis
  • 吞吐量:①对单位时间内完成的工作量(请求)的量度。②在GC中运行用户代码的时间站总运行时间的比例,(总运行时间=程序的运行时间+内存回收的时间)
  • 并发数:同一时刻,对服务器有实际交互的请求数
  • 内存占用:java堆区所占得内存大小

JVM监控工具-命令行

Java作为最流行的编程语言之一,其应用性能诊断一直收到业界广泛关注。可能造成Java应用出现性能问题的因素非常多,例如线程控制、磁盘读写、数据库访问、网络I/O、垃圾收集等。想要定位这些问题,一款优秀的性能诊断工具必不可少。

jps(Java Process Status)

查看正在运行的java进程

语法

jps [options] [hostid]

参数

  • options

-q:只显示本地虚拟机唯一id,不显示主类的名称等
-l:输出应用程序主类的全类名 或执行的是jar包则输出jar完整路径
-m:输出虚拟机进程启动时传递给主类的main()的参数
-v:列出虚拟机进程启动时的JVM参数。比如:-Xms -Xmx等

  • hostid

RMI注册表中注册的主机名。如果想要远程监控主机上的java程序,需要安装jstatd。
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易收到IP地址欺诈攻击
如果安全文件无法使用一个定制的策略文件来处理,那么最安全的操作时不运行jstatd服务器,而是在本地使用jstat和jps工具。

jstat(JVM Statistics Monitoring Tool)

查看JVM统计信息,用于监视虚拟机各种运行状态信息的命令行工具。他可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
在没有GUI图形界面,只提供了春文件控制台环境的服务器上,他将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄露问题
jstat还可以用来判断是否出现内存泄露。

  • 在长时间运行的java程序中,我们可以运行jstat命令连续获取多行性能数据,并取这几行数据中OU列(即已占用的老年代内存)的最小值。
  • 然后,我们每隔一段较长的时间重复一次上述操作,来获得多组OU的最小值。如果这些值呈上涨趋势,则说明该java程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄露。

语法

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

参数

  • option

-class:显示ClassLoader的相关信息,类的装载、卸载数量、总空间、类装载所消耗的时间等。

[root@hadoop01 ~]# jstat -class 58996
Loaded  Bytes  Unloaded  Bytes     Time   
  7136 15249.2        9    10.5       3.28

-gc:显示与GC先关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。

[root@hadoop01 ~]# jstat -gc 58996
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
 0.0   3072.0  0.0   3072.0 52224.0   5120.0   993280.0   146406.0  51656.0 45949.6 6856.0 6189.1     39    1.311   0      0.000    1.311

在这里插入图片描述

-gccapacity:显示内容与-gc基本相同,但输出主要关注java堆各个区域使用到的最大、最小空间

[root@hadoop01 ~]# jstat -gccapacity 58996
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC 
     0.0 1048576.0  55296.0    0.0 3072.0  52224.0        0.0  1048576.0   993280.0   993280.0      0.0 1093632.0  51656.0      0.0 1048576.0   6856.0     39     0

-gcutil:显示内容与-gc基本相同,但输出主要关注已使用空间站总空间的百分比。

[root@hadoop01 ~]# jstat -gcutil 58996
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00 100.00  54.90  14.74  88.95  90.27     39    1.311     0    0.000    1.311

-gccause:与 -gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。

[root@hadoop01 ~]# jstat -gccause 58996
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC                 
  0.00 100.00  56.86  14.74  88.95  90.27     39    1.311     0    0.000    1.311 G1 Evacuation Pause  No GC

-gcnew:显示新生代GC状况。

[root@hadoop01 ~]# jstat -gcnew 58996
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT  
   0.0 3072.0    0.0 3072.0 15  15 3584.0  52224.0  30720.0     39    1.311

-gcnewcapacity:显示内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间。

[root@hadoop01 ~]# jstat -gcnewcapacity 58996
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC 
       0.0  1048576.0    55296.0      0.0      0.0 1048576.0   3072.0  1048576.0    52224.0    39     0

-gcold:显示老年代GC状况

[root@hadoop01 ~]# jstat -gcold 58996
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT     GCT   
 51656.0  45949.6   6856.0   6189.1    993280.0    146406.0     39     0    0.000    1.311
  • interval

用于指定输出统计数据的周期,单位为毫秒。即:查询间隔

[root@hadoop01 ~]# jstat -class 58996 1000
Loaded  Bytes  Unloaded  Bytes     Time   
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  • count

用于指定查询的总次数

[root@hadoop01 ~]# jstat -class 58996 1000 5
Loaded  Bytes  Unloaded  Bytes     Time   
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
  7136 15249.2        9    10.5       3.28
[root@hadoop01 ~]#
  • -t

可以在输出信息前加上一个Timestamp列,显示程序的运行时间,单位,秒。
我们可以比较java进程的启动时间以及总GC时间(GCT列),或者两次测量的间隔时间以及总GC时间的增量,来得出GC时间占总运行时间的比例。
如果该比例超过20%,则说明目前堆空间的压力较大;
如果该比例超过90%,则说明堆里几乎没有可用空间,随时都可能抛出OOM异常。

[root@hadoop01 ~]# jstat -class -t 58996 1000 5
Timestamp       Loaded  Bytes  Unloaded  Bytes     Time   
        64975.3   7136 15249.2        9    10.5       3.28
        64976.4   7136 15249.2        9    10.5       3.28
        64977.4   7136 15249.2        9    10.5       3.28
        64978.3   7136 15249.2        9    10.5       3.28
        64979.4   7136 15249.2        9    10.5       3.28
  • -h

可以在周期性数据输出时,输出多少行数据后输出一个表头信息

[root@hadoop01 ~]# jstat -class -t -h3 58996 1000 5
Timestamp       Loaded  Bytes  Unloaded  Bytes     Time   
        65023.3   7136 15249.2        9    10.5       3.28
        65024.2   7136 15249.2        9    10.5       3.28
        65025.3   7136 15249.2        9    10.5       3.28
Timestamp       Loaded  Bytes  Unloaded  Bytes     Time   
        65026.3   7136 15249.2        9    10.5       3.28
        65027.3   7136 15249.2        9    10.5       3.28

jinfo(Configuration Info for Java)

实时查看和修改JVM配置参数,查看虚拟机配置参数信息,也可以用于调整虚拟机的配置参数。
在很多情况下,java应用程序不会指定所有的java虚拟机参数。而此时,开发人员可能不知道某一个具体的java虚拟机参数的默认值。在这种情况下,可能需要通过查询文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了jinfo工具,开发人员可以很方便的找到java虚拟机参数的当前值。
jinfo不仅可以查看某一个java虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。但是,并非所有参数都支持动态修改。参数只有被标记为manageable的flag可以被实时修改。其实这个修改能力是极其有限的。

语法

  • to connect to running process
    jinfo [option] <pid>
  • to connect to a core file
    jinfo [option] <executable <core>
  • to connect to remote debug server
    jinfo [option] [server_id@]<remote server IP or hostname>

参数

  • option

no option:输出全部的参数和系统属性。
-flag name:输出对应名称的参数。
-flags:输出全部的参数
-sysprops:输出系统的参数

-flag [±] name:开启或者关闭对应名称的参数,只有被标记为manageable的参数才可以被动态修改。(java -XX:+PrintFlagsFinal -version | grep manageable)
-flag name=value:设定对应名称的参数。

jmap(JVM Memory Map)

导出内存映像文件&内存使用情况。获取dump文件(推转储快照文件,二进制文件,指一个Java进程在某个时间点的内存快照),它还可以获取目标java进程的内存相关信息,包括java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。

  • to connect to running process
    jmap [option] <pid>
  • to connect to a core file
    jmap [option] <executable <core>
  • to connect to remote debug server
    jmap [option] [server_id@]<remote server IP or hostname>

参数

  • option

-dump:生成Java堆转储快照dump文件,-dump:live只保存堆中的存活对象
-heap:输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等。
-histo:输出推空间中对象的统计信息,包括类、实力数量和合计容量。-histo:live只统计堆中的存活对象
-finalizerinfo:显示在F-Queue中等待Finalizer线程执行fianlize方法的对象。
-permstat:以ClassLoader为统计口径输出永久代的内存状态信息。
-F:当虚拟机进程堆-dump选项没有任何响应时,强制执行生成dump文件。

导出内存映像文件

  • 手动方式

jmap -dump:format=b,file=<filename.hprof> <pid.>
jmap -dump:live,format=b,file=<filename.hprof> <pid.>

  • 自动方式

-XX:+HeapDumpOnOutOfMemoryError:在程序发生OOM时,导出应用程序的当前堆快照。
-XX:HeapDumpPath=<filename.hprof>:可以指定堆快照的保存位置

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代替

jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>

jstack(JVM Stack Trace)

用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。
生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况。

在Thread dump中,要留意下面几种状态:

  • 死锁,Deadlock(重点关注)
  • 等待资源,Waiting on condition(重点关注)
  • 等待获取监视器,Waiting on monitor entry(重点关注)
  • 阻塞,Blocked(重点关注)
  • 执行中,Runnable
  • 暂停,Suspended

语法

jstack option pid

参数

-F:当正常输出的情趣不被响应时,强制输出线程堆栈
-l:除堆栈外,显示关于锁的附加信息
-m:如果调用到本地方法的话,可以显示C/C++的堆栈
-h:帮助操作

在java代码层面使用如下方式也可打印线程的堆栈信息

Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();
Set<Map.Entry<Thread, StackTraceElement[]>> entries = all.entrySet();
for (Map.Entry<Thread, StackTraceElement[]> en : entries) {
	Thread t = en.getKey();
	StackTraceElement[] v = en.getValue();
	log.info("【Thread name is {}:】", t.getName());
	for (StackTraceElement s : v) {
		log.info(s.toString());
	}
}

jcmd

多功能命令行,在JDK1.7以后,新增了一个命令行工具jcmd。他是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。
jcmd拥有jmap的大部分功能,并且在Oracle的官方网站上也推荐使用jcmd命令代jmap命令

语法

jcmd <option>

参数

  • option

jcmd -l:列出所有的JVM进程

[chao@hadoop01 logs]$ jcmd
3031 kafka.Kafka /opt/module/kafka/config/server.properties
3389 sun.tools.jcmd.JCmd

jcmd pid help:针对指定的进程,列出支持的所有命令

[chao@hadoop01 logs]$ jcmd 3031 help
3031:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
VM.classloader_stats
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.finalizer_info
GC.heap_info
GC.run_finalization
GC.run
VM.uptime
VM.dynlibs
VM.flags
VM.system_properties
VM.command_line
VM.version
help

For more information about a specific command use 'help <command>'.

jcmd pid 具体命令:显示指定进程的指令命令的数据

jstatd

远程主机信息收集。之前的指令只涉及到监控本机的Java应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如jps、jstat)。为了启用远程监控,则需要配合使用jstatd工具。
命令jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。jstatd服务器将本机的Java应用程序信息传递到远程计算机。
在这里插入图片描述

JVM监控工具-GUI工具

使用上述命令行工具或组合能帮你获取目标Java应用性能相关的基础信息,但他们存在一下局限:

  1. 无法获取方法级别的分析数据,如方法间的调用关系、各方法的调用次数和调用时间等(这对定位应用性能瓶颈至关重要)。
  2. 要求用户登录到目标Java应用所在的宿主机上,使用起来不是很方便。
  3. 分析数据通过终端输出,结果展示不够直观。
    为此,JDK提供了一些内存泄露的分析工具,如jconsole,jvisualvm等,用于辅助开发人员定位问题。

JDK自带的工具

  • jconsole:JDK自带的可视化监控工具。查看Java应用程序的运行概况、监控堆信息、永久区(或元空间)使用情况、类加载情况等。JAVA_HOME\bin\jconsole.exe。
  • Visual VM:提供了一个可视界面,用于查看Java虚拟机上运行的基于Java技术的应用程序的详细信息。JAVA_HOME\bin\jvisualvm.exe
  • JMC:Java Mission Control,内置Java Flight Recorder。能够以极低的性能开销收集Java虚拟机的性能数据。

第三方工具

  • MAT:Memory Analyzer Tool是基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,他可以帮助我们查找内存泄露和减少内存消耗。Eclipse的插件形式。
  • JProfiler:商业软件,需要付费。功能强大。
  • Arthas:阿尔萨斯,Alibaba开源的Java诊断工具。https://github.com/alibaba/arthas/releases
  • Btrace:Java运行时追踪工具。可以在不停机的情况下,跟踪指定的方法调用、构造函数调用和系统内存等信息。

工具概述

Visual VM

概述
  • Visual VM 是一个功能强大的多合一故障诊断和性能监控的可视化工具。
  • 他集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替jconsole。
  • 在JDK 6 Update 7 以后,Visual VM便作为JDK的一部分发布(VisualVM 在JDK/bin目录下)
  • 此外Visual VM也可以作为独立的软件安装。
链接方式
  • 远程链接

1.确定远程服务器的IP地址。
2.添加JMX(通过JMX技术具体监控远端服务器哪个Java进程)
3.修改bin/catalina.sh文件,链接远程的tomcat
4.在…/conf中添加jmxremote.access和jmxremote.password文件
5.将服务器地址改为公网ip地址
6.设置阿里云安全策略和防火墙策略
7.启动Tomcat,查看Tomcat启动日志和端口监听
8.JMX中输入端口号、用户名、密码登录

主要功能
  • 生成、读取堆内存快照
  • 查看JVM参数和系统属性
  • 查看运行中的虚拟机进程
  • 生成、读取线程快照
  • 程序资源的实时监控
  • 其它功能(JMX代理连接、远程环境监控、CPU分析和内存分析)

Arthas

阿尔萨斯是阿里巴巴开源的Java诊断工具,在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。
支持JDK 6+,支持Linux/max/windows,采用命令行交互模式,同时提供丰富的Tab自动补全功能,进一步方便进行问题的定位和诊断。

安装

https://arthas.aliyun.com/doc/

  • 在/usr/local/bin目录下执行
[root@hadoop01 bin]# curl -L https://arthas.aliyun.com/install.sh | sh
  • 执行as.sh
[root@hadoop01 bin]# as.sh 
Arthas script version: 3.5.2
[INFO] JAVA_HOME: /opt/module/jdk1.8.0_261
[INFO] Process 58996 already using port 3658
[INFO] Process 58996 already using port 8563
Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 58996 kafka.Kafka
  [2]: 3992 org.apache.zookeeper.server.quorum.QuorumPeerMain
  • 选择需要监控的进程序号[1]
Attach success.
telnet connecting to arthas server... current timestamp is 1626938671
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.                           
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'                          
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.                          
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |                         
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'                          
                                                                                

wiki       https://arthas.aliyun.com/doc                                        
tutorials  https://arthas.aliyun.com/doc/arthas-tutorials.html                  
version    3.5.2                                                                
main_class                                                                      
pid        58996                                                                
time       2021-07-22 15:22:49                                                  

[arthas@58996]$ 
  • 退出当前客户端:quit/exit
  • 关闭arthas服务端,并退出所有客户端:stop/shutdown
基础指令

常用操作命令详见官方文档https://arthas.aliyun.com/doc/

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值