侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

jvm中常用的性能监控和调优工具介绍与使用

孔子说JAVA
2021-09-14 / 0 评论 / 0 点赞 / 138 阅读 / 18,553 字 / 正在检测是否收录...

1、引言

企业级Java开发中,有时候我们会碰到下面这些问题:

  • OutOfMemoryError,内存不足
  • 内存泄露
  • 线程死锁
  • 锁争用(Lock Contention)
  • Java进程消耗CPU过高

这些问题在日常开发中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存,而不会深究问题根源),但能够理解并解决这些问题是Java程序员进阶的必备要求。本文将对一些常用的JVM性能调优监控工具进行介绍。

2、性能监控工具概述

工具主要是为了解决问题而生的,在JDK安装目录,就有很多这样的工具。

image-1649223216370

JVM调优工具简介

工具名称 主要作用
jps(JVM Process Status Tool) 显示指定系统中所有的HotSpot虚拟机进程
jstat(JVM Statistics Monitoring Tool) 手机HotSpot虚拟机各方面的运行数据
jinfo(Configuration Info for Java) 显示虚拟机配置信息
jmap(Memory Map for Java) 生成虚拟机的内存存储快照(heap dump文件)
jhat(JVM Heap Dump Browser) 分析内存存储快照,不推荐使用,消耗资源而且慢
jstack(Stack Trace for Java) 显示虚拟机的线程快照
JConsole JMX的可视化管理工具
VisualVM 多合一故障管理工具

3、使用方式及应用

3.1 jps:虚拟机进程状况工具

jps主要用来输出JVM中运行的进程状态信息。

3.1.1 语法格式

jps [options] [hostid]

第一个参数:options

-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数

第二个参数:hostid

主机或者是服务器的id,如果不指定,就默认为当前的主机或者是服务器。

3.1.2 用例

image-1649223348407

3.2 jstat:虚拟机统计信息监控工具

jstat监视虚拟机各种运行状态信息,可以显示本地或者是远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

命令选项 涵义
-class 监视类装载、卸载数量、总空间及类装载所耗费的时间
-gc 监视java堆状况,包括Eden区、2个survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息
-gccapacity 监视内容与-gc基本相同,但输出主要关注java堆各个区域使用到的最大和最小空间
-gcutil 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause 与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因
-gcnew 监视新生代GC的情况
-gcnewcapacity 监视内容与-gcnew基本相同,输出主要关注使用到的最大和最小空间
-gcold 监视老年代GC的情况
-gcoldcapacity 监视内容与-gcold基本相同,输出主要关注使用到的最大和最小空间
-gcpermcapacity 输出永久代使用到的最大和最小空间(java8之前版本)
-gcmetacapacity 元数据空间统计(java8及之后版本)
-compiler 输出JIT编译期编译过的方法、耗时等信息
-printcompilation 输出已经被JIT编译的方法

3.2.1 语法格式

jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]

jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]
 
jstat -class 3176 1000 10(每隔1s,共输出10次)

第一个参数:generalOption | outputOptions
这个参数表示的option,代表着用户希望查询的虚拟机信息,分为类加载、垃圾收集、运行期编译状况3类。

第二个参数:vmid
vmid是Java虚拟机ID,即当前运行的java进程号,在Linux/Unix系统上一般就是进程ID。

第三个参数:interval
interval是采样时间间隔,单位为秒或毫秒。

第四个参数:count
count表示的是采样数,即打印次数,如果缺省则打印无数次。

3.2.2 用例

1)-class 查看类加载信息

image-1649223498141

  • Loaded:加载class的数量
  • Bytes:所占用空间大小
  • Unloaded:未加载数量
  • Bytes:未加载占用空间
  • Time:时间

2)-gcutil 总结垃圾回收统计

image-1649223537745

  • S0: 新生代中Survivor space 0区(幸存1区)已使用空间的百分比
  • S1: 新生代中Survivor space 1区(幸存2区)已使用空间的百分比
  • E: 新生代(伊甸园区)已使用空间的百分比
  • O: 老年代已使用空间的百分比
  • M:元数据区使用比例
  • CCS:压缩使用比例
  • YGC: 从应用程序启动到当前,发生Yang GC 的次数(年轻代垃圾回收次数)
  • YGCT: 从应用程序启动到当前,Yang GC所用的时间【单位秒】
  • FGC: 从应用程序启动到当前,发生Full GC的次数(老年代垃圾回收次数)
  • FGCT: 从应用程序启动到当前,Full GC所用的时间
  • GCT: 从应用程序启动到当前,用于垃圾回收的总时间【单位秒】

3)-gc 查看垃圾回收情况

image-1649223565685

  • S0C:第一个幸存区的大小
  • S1C:第二个幸存区的大小
  • S0U:第一个幸存区的使用大小
  • S1U:第二个幸存区的使用大小
  • EC:伊甸园区的大小
  • EU:伊甸园区的使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • MC:方法区大小
  • MU:方法区使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

4)-gccause 查看垃圾回收情况

image-1649223585437

  • S0 — Heap上的 Survivor space 0 区已使用空间的百分比
  • S1 — Heap上的 Survivor space 1 区已使用空间的百分比
  • E — Heap上的 Eden space 区已使用空间的百分比
  • O — Heap上的 Old space 区已使用空间的百分比
  • P — Perm space 区已使用空间的百分比
  • YGC — 从应用程序启动到采样时发生 Young GC 的次数
  • YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
  • FGC — 从应用程序启动到采样时发生 Full GC 的次数
  • FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
  • GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)

5)-gcnew 新生代垃圾回收统计

image-1649223613554

  • S0C:第一个幸存区大小
  • S1C:第二个幸存区的大小
  • S0U:第一个幸存区的使用大小
  • S1U:第二个幸存区的使用大小
  • TT:对象在新生代存活的次数
  • MTT:对象在新生代存活的最大次数
  • DSS:期望的幸存区大小
  • EC:伊甸园区的大小
  • EU:伊甸园区的使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间

6) -gcold 老年代垃圾回收统计

image-1649223638021

  • MC:方法区大小
  • MU:方法区使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

7)-compiler 查看JIT编译信息

image-1649223655350

  • Compiled:编译数量。
  • Failed:失败数量
  • Invalid:不可用数量
  • Time:时间
  • FailedType:失败类型
  • FailedMethod:失败的方法

8)-gccapacity 堆内存统计

image-1649223677760

  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0C:第一个幸存区大小
  • S1C:第二个幸存区的大小
  • EC:伊甸园区的大小
  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • OC:当前老年代大小
  • MCMN:最小元数据容量
  • MCMX:最大元数据容量
  • MC:当前元数据空间大小
  • CCSMN:最小压缩类空间大小
  • CCSMX:最大压缩类空间大小
  • CCSC:当前压缩类空间大小
  • YGC:年轻代gc次数
  • FGC:老年代GC次数

9)-gcnewcapacity 新生代内存统计

image-1649223692948

  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0CMX:最大幸存1区大小
  • S0C:当前幸存1区大小
  • S1CMX:最大幸存2区大小
  • S1C:当前幸存2区大小
  • ECMX:最大伊甸园区大小
  • EC:当前伊甸园区大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代回收次数

10)-gcoldcapacity 老年代内存统计

image-1649223706703

  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • OC:老年代大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

11)-gcmetacapacity 元数据空间统计

image-1649223746856

  • MCMN: 最小元数据容量
  • MCMX:最大元数据容量
  • MC:当前元数据空间大小
  • CCSMN:最小压缩类空间大小
  • CCSMX:最大压缩类空间大小
  • CCSC:当前压缩类空间大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

12)-printcompilation JVM编译方法统计

image-1649223766102

  • Compiled:最近编译方法的数量
  • Size:最近编译方法的字节码数量
  • Type:最近编译方法的编译类型。
  • Method:方法名标识。

3.2.3 jstat的局限性

没有提供有关GC活动的丰富详细信息。 例如,从jstat中您将不知道:

  • 如果一次样本中报告了多个GC事件,则我们将不知道每个GC事件的暂停时间是多少。
  • 用户(即Java层),系统(即内核)和用户花费了多少时间。
  • 有多少个GC线程正在工作,并占用了多少时间?
  • 一个GC事件具有几个子阶段(例如初始标记,清理,备注,并发标记……)。 无法提供信息分类。
  • 每个GC事件回收多少字节。
  • 有时,jstat报告的数据也会产生误导 。

如果您想进行准确的GC分析,GC日志是更可靠的方法。

3.3 jinfo:显示虚拟机配置信息

jinfo(Configuration Info for Java),显示虚拟机配置信息,实时地查看和调整虚拟机各项参数。

3.3.1 语法格式

jinfo [option] pid
第一个参数:option
主要选项:
no option 输出全部的参数和系统属性
-flag name 输出对应名称的参数
-flag [+|-]name 开启或者关闭对应名称的参数
-flag name=value 设定对应名称的参数
-flags 输出全部的参数
-sysprops 输出系统属性

第二个参数:pid
指定显示的进程id。

3.3.2 用例

1) jinfo pid

输出当前 jvm 进程的全部参数和系统属性。

image-1649223853766

2) jinfo -flag name pid

输出对应名称的参数。

image-1649223866865

3) jinfo -flag [+|-]name pid

开启或者关闭对应名称的参数,使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用。

image-1649223887057

4) jinfo -flag name=value pid

修改指定参数的值。同示例三,但示例三主要是针对 boolean 值的参数设置的。如果是设置 value值,则需要使用 name=value 的形式。jinfo虽然可以在java程序运行时动态地修改虚拟机参数,但并不是所有的参数都支持动态修改。

image-1649223907247

5) jinfo -flags pid

输出全部的参数。

image-1649223918300

6) jinfo -sysprops pid

输出当前 jvm 进行的全部的系统属性。

image-1649223929808

3.4 jmap:生成虚拟机的内存存储快照(heap dump文件)

jmap(Memory Map for Java),内存映像工具,用于生成堆转存的快照,一般是heapdump或者dump文件。如果不使用jmap命令,可以使用-XX:+HeapDumpOnOutOfMemoryError参数,当虚拟机发生内存溢出的时候可以产生快照。或者使用kill -3 pid也可以产生。

  • jmap的作用并不仅仅是为了获取dump文件,也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列,java堆和永久代的详细信息,如空间使用率,当前用的哪种收集器。

3.4.1 语法格式

jmap [option] vmid

option: 选项参数。
pid: 需要打印配置信息的进程ID。
executable: 产生核心dump的Java可执行文件。
core: 需要打印配置信息的核心文件。
server-id 可选的唯一id,如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。
remote server IP or hostname 远程调试服务器的IP地址或主机名。

option
no option: 查看进程的内存映像信息,类似 Solaris pmap 命令。
heap: 显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况等,只在Linux/Solaris平台下有效。
histo[:live]: 显示堆中对象的统计信息,包括类、实例数量、合计容量。
clstats:打印类加载器信息
finalizerinfo: 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象,只在Linux/Solaris平台下有效。
dump:<dump-options>:生成java堆转储快照,格式为-dump:[live, ]format=b, file=<filename>, 其中live子参数说明是否只dump出存活的对象。
permstat: 以ClassLoader为统计口径显示永久代内存状态,只在Linux/Solaris平台下有效。
F: 当虚拟机进程对-dump没有响应时,可使用这个选项强制生成dump快照。使用-dump或者-histo参数. 在这个模式下,live子参数无效.
help:打印帮助信息
J<flag>:指定传递给运行jmap的JVM的参数

3.4.2 用例

image-1649223980165

1)jmap pid

查看进程的内存映像信息,类似 Solaris pmap 命令。使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。

image-1649224007271

2)jmap -heap pid

显示Java堆详细信息。打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息。

image-1649224014178

C:\Users\server>jmap -heap 14268
Attaching to process ID 14268, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.111-b14

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 5368709120 (5120.0MB)
   NewSize                  = 2147483648 (2048.0MB)
   MaxNewSize               = 2147483648 (2048.0MB)
   OldSize                  = 3221225472 (3072.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 524288000 (500.0MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 792723456 (756.0MB)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 2145386496 (2046.0MB)
   used     = 1743475808 (1662.7080993652344MB)
   free     = 401910688 (383.2919006347656MB)
   81.26628051638487% used
From Space:
   capacity = 1048576 (1.0MB)
   used     = 786432 (0.75MB)
   free     = 262144 (0.25MB)
   75.0% used
To Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
PS Old Generation
   capacity = 3221225472 (3072.0MB)
   used     = 416272888 (396.98876190185547MB)
   free     = 2804952584 (2675.0112380981445MB)
   12.922811259826025% used

31993 interned Strings occupying 3071760 bytes.

3)jmap -histo:live pid

显示堆中对象的统计信息。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。

  • 采用 jmap -histo pid>a.log 日志将其保存,在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。
  • jmap -dump:format=b,file=outfile 3024可以将3024进程的内存heap输出出来到outfile文件里,再配合MAT(内存分析工具)。

image-1649224080642

class name是对象类型,说明如下:

  • B  byte
  • C  char
  • D  double
  • F  float
  • I  int
  • J  long
  • Z  boolean
  • [  数组,如[I表示int[]
  • [L+类名 其他对象

4)jmap -clstats pid

打印类加载器信息。-clstats是-permstat的替代方案,在JDK8之前,-permstat用来打印类加载器的数据,打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。

image-1649224144660

5)jmap -finalizerinfo pid

打印等待终结的对象信息。Number of objects pending for finalization: 0 说明当前F-QUEUE队列中并没有等待Fializer线程执行final。

image-1649224152205

6)jmap -dump:format=b,file=heapdump.phrof pid

生成堆转储快照dump文件。以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。

  • 这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用, 线上系统慎用。

image-1649224163634

3.5 jhat:分析内存存储快照

jhat(JVM Heap Dump Browser/Java Heap Analysis Tool) ,是java虚拟机自带的一种虚拟机堆转储快照分析工具,不推荐使用,消耗资源而且慢。

  • 可以用jhat命令将dump出来的hprof文件转成html的形式,然后通过http访问可以查看堆情况。
  • 通过浏览器访问http://localhost:7000则可以看到如下信息,显示jvm中所有非平台类信息。通过这些连接可以进一步查看所有类信息(包括JAVA平台的类)、所有类的实例数量以及实例的基本信息。最后,还有一个连接指向OQL查询页面。

3.5.1 语法格式

jhat C:\Users\server\heapdump.phrof
phrof 文件路径

3.5.2 用例

image-1649224225383

image-1649224235365

这样就启动起来了一个简易的HTTP服务,端口号是7000,尝试一下用浏览器访问一下它,本地的可以通过http://localhost:7000,就可以得到这样的页面:

image-1649224243237

All classes including platform 把所有类信息显示出来(默认是不包括Java平台的类)

查看堆异常主要关注两个:

  • Show instance counts for all classes (excluding platform) 平台外的所有对象信息
  • Show heap histogram 显示堆的统计信息

image-1649224297033

image-1649224287168

image-1649224304962

3.6 jstack:显示虚拟机的线程快照

jstack(Stack Trace for Java) ,主要用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

  • 另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

3.6.1 语法格式

$jstack [ option ] pid
$jstack [ option ] executable core
$jstack [ option ] [server-id@]remote-hostname-or-IP

参数说明:
pid: java应用程序的进程号,一般可以通过jps来获得;
executable:产生core dump的java可执行程序;
core:打印出的core文件;
remote-hostname-or-ip:远程debug服务器的名称或IP;
server-id: 唯一id,假如一台主机上多个远程debug服务;

3.6.2 用例

查看输出 jstack –l pid

通过jstack输出的线程信息主要包括:jvm自身线程、用户线程等。其中jvm线程会在jvm启动时就会存在。对于用户线程则是在用户访问时才会生成。

1)jvm线程:

在线程中,有一些 JVM内部的后台线程,来执行譬如垃圾回收,或者低内存的检测等等任务,这些线程往往在JVM初始化的时候就存在,如下所示:

"Attach Listener" daemon prio=10 tid=0x0000000052fb8000 nid=0xb8f waiting on condition [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE



   Locked ownable synchronizers:

        - None

destroyJavaVM" prio=10 tid=0x00002aaac1225800 nid=0x7208 waiting on condition [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE



   Locked ownable synchronizers:

        - None

2)用户级别的线程

还有一类线程是用户级别的,它会根据用户请求的不同而发生变化。该类线程的运行情况往往是我们所关注的重点。而且这一部分也是最容易产生死锁的地方。

"qtp496432309-42" prio=10 tid=0x00002aaaba2a1800 nid=0x7580 waiting on condition [0x00000000425e9000]

   java.lang.Thread.State: TIMED_WAITING (parking)

        at sun.misc.Unsafe.park(Native Method)

        - parking to wait for  <0x0000000788cfb020> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)

        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)

        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)

        at org.eclipse.jetty.util.BlockingArrayQueue.poll(BlockingArrayQueue.java:320)

        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:479)

        at java.lang.Thread.run(Thread.java:662)



   Locked ownable synchronizers:

        - None

从上述的代码示例中我们可以看到该用户线程的以下几类信息:

  • Ø 线程的状态:waiting on condition(等待条件发生)
  • Ø 线程的调用情况;
  • Ø 线程对资源的锁定情况:Locked

3.6.3 jstack检测死锁

1)死锁代码

public class DeadLock {
    private static Object objA = new Object();
    private static Object objB = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(new Thread1());
        Thread thread2 = new Thread(new Thread2());
        thread1.start();
        thread2.start();
    }

    private static class Thread1 implements Runnable{
        @Override
        public void run() {
            synchronized (objA) {
                System.out.println("线程1得到A对象的锁");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (objB) {
                    System.out.println("线程1得到B对象的锁");
                }
            }
        }
    }

    private static class Thread2 implements Runnable{
        @Override
        public void run() {
            synchronized (objB) {
                System.out.println("线程2得到B对象的锁");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (objA) {
                    System.out.println("线程2得到A对象的锁");
                }
            }
        }  
    }
}

2)运行结果只能看到两个线程各只拿到了一个锁,在一直等待对方的锁释放。

  • 线程1得到A对象的锁
  • 线程2得到B对象的锁

**3)使用 jps 来查看对应的 PID **

C:\Users\server>jps
4240 Jps
480 DeadLock

4)使用 jstack PID 来查看其线程情况:

C:\Users\server>jstack 480

Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.45-b08 mixed mode):

"DestroyJavaVM" prio=6 tid=0x00000000047c1000 nid=0x9878 waiting on condition [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

"Thread-1" prio=6 tid=0x0000000010aa3000 nid=0xafa0 waiting for monitor entry [0x000000001105f000]

   java.lang.Thread.State: BLOCKED (on object monitor)

        at com.zaimeibian.Test$Thread2.run(Test.java:46)

        - waiting to lock <0x00000007c099cc20> (a java.lang.Object)

        - locked <0x00000007c099cc30> (a java.lang.Object)

        at java.lang.Thread.run(Thread.java:744)

"Thread-0" prio=6 tid=0x0000000010aa2800 nid=0xae74 waiting for monitor entry [0x0000000010f5f000]

   java.lang.Thread.State: BLOCKED (on object monitor)

        at com.zaimeibian.Test$Thread1.run(Test.java:27)

        - waiting to lock <0x00000007c099cc30> (a java.lang.Object)

        - locked <0x00000007c099cc20> (a java.lang.Object)

        at java.lang.Thread.run(Thread.java:744)

"Service Thread" daemon prio=6 tid=0x000000000f10a000 nid=0x9a8c runnable [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=10 tid=0x000000000f109800 nid=0xaf28 waiting on condition [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=10 tid=0x000000000f105800 nid=0x85dc waiting on condition [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

"Attach Listener" daemon prio=10 tid=0x000000000f104800 nid=0xac04 waiting on condition [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=10 tid=0x000000000f102000 nid=0xa678 runnable [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=8 tid=0x000000000f0bd000 nid=0xaed8 in Object.wait() [0x000000001045f000]

   java.lang.Thread.State: WAITING (on object monitor)

        at java.lang.Object.wait(Native Method)

        - waiting on <0x00000007c0905568> (a java.lang.ref.ReferenceQueue$Lock)

        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)

        - locked <0x00000007c0905568> (a java.lang.ref.ReferenceQueue$Lock)

        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)

        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)

"Reference Handler" daemon prio=10 tid=0x000000000f0b2000 nid=0xaedc in Object.wait() [0x000000001035f000]

   java.lang.Thread.State: WAITING (on object monitor)

        at java.lang.Object.wait(Native Method)

        - waiting on <0x00000007c09050f0> (a java.lang.ref.Reference$Lock)

        at java.lang.Object.wait(Object.java:503)

        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)

        - locked <0x00000007c09050f0> (a java.lang.ref.Reference$Lock)

"VM Thread" prio=10 tid=0x000000000f0b0000 nid=0xaef0 runnable

"GC task thread#0 (ParallelGC)" prio=6 tid=0x00000000047d6000 nid=0xacb0 runnable

"GC task thread#1 (ParallelGC)" prio=6 tid=0x00000000047d8000 nid=0xaee0 runnable

"GC task thread#2 (ParallelGC)" prio=6 tid=0x00000000047d9800 nid=0xaed4 runnable

"GC task thread#3 (ParallelGC)" prio=6 tid=0x00000000047db000 nid=0xac54 runnable

"VM Periodic Task Thread" prio=10 tid=0x000000000f132000 nid=0xaff0 waiting on condition

JNI global references: 105

Found one Java-level deadlock:

=============================

"Thread-1":

  waiting to lock monitor 0x000000000f0ba488 (object 0x00000007c099cc20, a java.lang.Object),

  which is held by "Thread-0"

"Thread-0":

  waiting to lock monitor 0x000000000f0bcf28 (object 0x00000007c099cc30, a java.lang.Object),

  which is held by "Thread-1"

Java stack information for the threads listed above:

===================================================

"Thread-1":

        at com.zaimeibian.Test$Thread2.run(Test.java:46)

        - waiting to lock <0x00000007c099cc20> (a java.lang.Object)

        - locked <0x00000007c099cc30> (a java.lang.Object)

        at java.lang.Thread.run(Thread.java:744)

"Thread-0":

        at com.zaimeibian.Test$Thread1.run(Test.java:27)

        - waiting to lock <0x00000007c099cc30> (a java.lang.Object)

        - locked <0x00000007c099cc20> (a java.lang.Object)

        at java.lang.Thread.run(Thread.java:744)

Found 1 deadlock.

可以看到 jstack 打印出了线程的状态,而且发现一个死锁。另外,线程状态有以下几种:

  • RUNNABLE 线程运行中或 I/O 等待
  • BLOCKED 线程在等待 monitor 锁( synchronized 关键字)
  • TIMED_WAITING 线程在等待唤醒,但设置了时限
  • WAITING 线程在无限等待唤醒

jstack详细用法见《jstack和线程dump分析

3.7 JConsole:JMX的可视化管理工具

JConsole,JMX的可视化管理工具。这个工具相比较前面几个工具,使用率比较高,很重要。它是一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。用java写的GUI程序,用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。

  • 在cmd里面输入 jconsole,选则进程就可以了。(也可以在jdk安装bin目录下找到jconsole.exe执行)。

image-1649224690088

当JConsole成功建立连接,它从连接上的JMX代理处获取信息,并且以下面几个标签页呈现信息。

  • Summary tab(概览). 监控JVM和一些监控变量的信息。
  • Memory tab(内存). 内存使用信息
  • Threads tab(线程). 线程使用信息
  • Classes tab(类). 类调用信息
  • VM tab(VM概要). JVM的信息
  • MBeans tab(MBean).所有MBeans的信息

3.7.1 概览

概述选项卡中显示CPU使用率,内存使用率,线程数,Java VM中加载的类的监控信息。

image-1649224722852

3.7.2 内存

监控内存消耗,提供了内存消耗和内存池的信息,相当于可视化的jstat命令。

image-1649224730209

内存标签功能“执行GC”的按钮,可以单击执行垃圾收集。 图表动态显示内存使用的堆和非堆内存的内存池。 可用的内存池取决于正在使用的版本的Java VM。 串行垃圾回收的内存池的HotSpot Java虚拟机,有以下几种。

  • 伊甸园空间(堆):大多数对象最初分配内存的池。
  • 生存空间(堆):包含伊甸园空间垃圾收集后生存的对象。
  • 年老代(堆):池包含已经存在一段时间的对象。
  • 永久代(非堆):池包含的所有虚拟机本身的反射的数据,如类和方法的对象。 Java虚拟机,使用类数据共享,这一代分为只读和读写区域。
  • 代码缓存(非堆):HotSpot Java虚拟机的还包括一个代码缓存,包含内存,使用本机代码的编译和存储。

“详细信息”区域显示了当前内存信息:

  • 已使用:目前使用的内存量,包括所有对象,可达和不可达占用的内存。
  • 已提交-分配 :保证由Java虚拟机使用的内存量。 提交的内存量可能会随时间而改变。 Java虚拟机可能会释放系统内存,并已提交的内存量可能会少于最初启动时分配的内存量。 提交的内存量将始终大于或等于使用的内存量。
  • 最大值,可用于内存管理的最大内存量。 它的价值可能会发生变化,或者是不确定的。 如果Java虚拟机试图增加使用的内存要大于提交的内存,内存分配可能失败,即使使用量小于或等于最大值(例如,当系统上的虚拟内存不足)。
  • GC时间 :累计时间花在垃圾收集和调用的总数。 它可能有多个行,其中每一个代表一个垃圾收集器算法在Java虚拟机使用时间。

GC时会暂停整个JAVA应用,普通GC 只是对年轻代进行垃圾回收,full的GC会对整个堆内存(包含老年代、年轻代)进行垃圾回收。system的GC显示调用GC。

full gc 只会在两个情况下发生:1)system.gc被显示调用时,会执行full gc。2)老年代的堆内存满时,会执行full gc。

较低的右侧的条形图显示堆和非堆内存中的内存池消耗的内存。 列会变成红色时,使用的内存超过了内存使用阀值。

3.7.3 线程

线程选项卡上提供了有关线程使用的信息。

image-1649224809738

在左下角的“线程”列表列出了所有的活动线程。 如果你输入一个“过滤器”字段中的字符串,线程列表将只显示其名称中包含你输入字符串线程。 点击一个线程在线程列表的名称,显示该线程的信息的权利,包括线程的名称,状态、阻塞和等待的次数、堆栈跟踪。

图表显示活动线程的数量随着时间的推移。 两行显示。

  • 红色 :峰值线程数
  • 蓝 :活动线程数。

检测死锁

要检查如果您的应用程序已经陷入了僵局运行(例如,您的应用程序似乎是挂了),死锁的线程可以通过点击“检测死锁”按钮检测。 如果检测到任何死锁的线程,这些都显示在一个新的死锁标签。

image-1649224876689

3.7.4 类

“类”标签显示关于类加载的信息。

image-1649224891781

图表曲线加载的类的数量随着时间的推移。

  • 红线总数(包括后来卸载的)加载的类。
  • 蓝线是当前的类加载。

在选项卡底部的详细信息部分显示类的加载,包括已 加载当前类,已加载类总数、已卸载类总数 。 跟踪类加载详细的输出,您可以勾选在顶部的右上角复选框。

3.7.5 VM概要

VM摘要选项卡提供了对Java虚拟机的信息。

image-1649224936650

在此选项卡中提供的信息包括以下内容。

1)摘要

  • 运行时间 :开始以来,Java虚拟机的时间总额。
  • 进程的CPU时间 :Java VM的开始,因为它消耗的CPU时间总量。
  • 编译总时间 :累计时间花费在JIT编译。

2)主题

  • 活动线程 :目前现场守护线程,加上非守护线程数量。
  • 峰值 :活动线程的最高数目,因为Java虚拟机开始。
  • 守护线程 :当前的活动守护线程数量。
  • 总线程 :开始自Java虚拟机启动的线程总数,包括非守护进程,守护进程和终止的线程。

3)类

  • 当前类装载 :目前加载到内存中的类数目。
  • 总类加载 :从Java VM开始加载到内存中的类总和,包括那些后来被卸载的类。
  • 已卸载类总数 :从Java虚拟机开始从内存中卸载的类的数目。

4)内存

  • 当前的堆大小 :目前所占用的堆的千字节数。
  • 分配的内存 :堆分配的内存总量。
  • 最大堆最大值 :堆所占用的千字节的最大数目。
  • 待最后确定的对象:待最后确定的对象的数量。
  • 花在执行GC的垃圾收集器 :包括垃圾收集,垃圾收集器的名称,进行藏品的数量和总时间的信息。

5)操作系统

  • 总物理内存
  • 空闲物理内存
  • 分配的虚拟内存

6)其他信息

  • VM参数 :输入参数的应用程序通过Java虚拟机,不包括的主要方法的参数。
  • 类路径是由系统类加载器用于搜索类文件的类路径。
  • 库路径 :加载库时要搜索的路径列表。
  • 引导类路径 :引导类路径是由引导类加载器用于搜索类文件。

3.7.6 MBean

MBeans选项卡显示的信息平台MBean服务器中的一个通用的方法对所有已注册的MBean。 MBeans选项卡允许您访问平台MXBean。 此外,您还可以监控和管理您的应用程序的MBean。

image-1649225003802

左侧的树显示当前正在运行的所有MBean。 当您选择树中的一个MBean, 其 MBeanInfo及其MBean描述符都显示在右侧,并在它下面的树中出现的任何属性,操作或通知。

image-1649225014687

image-1649225022617

3.8 VisualVM:多合一故障管理工具

VisualVM,多合一故障管理工具。同jconsole都是一个基于图形化界面的、可以查看本地及远程的JAVA GUI监控工具,Jvisualvm同jconsole的使用方式一样,直接在命令行打入jvisualvm即可启动,jvisualvm界面更美观一些,数据更实时。(也可以在jdk安装bin目录下找到jvisualvm.exe执行)

image-1649225040943

VisualVM工具基本涵盖内存分析jmap、线程分析jstack等功能,并且是可视化的,可以远程连接生产环境的服务器进行分析。主要功能如下:

  • 概述:显示JVM的一些基本信息。
  • 监视:【执行垃圾回收】、【堆Dump】、CPU、内存、类、线程、Metaspace元数据空间。堆dump:类、实例数、OQL控制台。
  • 线程:线程dump,类似于jstack
  • 抽样器:对【CPU】和【内存】进行采样。

3.8.1 概述

显示JVM的一些基本信息。

image-1649225076453

3.8.2 监视

查看CPU、内存、类、线程运行信息。

image-1649225084985

3.8.3 线程

查看线程详细信息。

image-1649225098608

  • 也可以点击右上角的dump按钮,将线程的信息导出,其实就是执行的jstack命令。

image-1649225109369

3.8.4 抽样器

抽样器可以对CPU、内存在一段时间内进行抽样,以供分析。

image-1649225122802

3.8.5 监控远程的JVM

VisualVM不仅可以监控本地的JVM进程,还可以监控远程的JVM进程,需要借助于JMX技术实现。JMX可以跨操作系统,也就是Windows和Linux系统可以相互进行网络传输。

1)添加远程主机

在远程选项卡上右键,点击添加远程主机。

image-1649225139954

2)添加JMX连接

在已添加的远程主机上右键,点击“添加JMX连接”。

image-1649225150589

image-1649225159215

连接成功后,使用方式和本地一样。

0

评论区