文章目录
- 1. 概述
- 2. jps:查看正在运行的Java进程
- 3. jstat:查看JVM统计信息
- 4. jinfo:实时查看和修改JVM配置参数
- 5. jmap:导出内存映像文件&内存使用情况
- 6、jhat:JDK自带堆分析工具
- 7 jstack:打印JVM中线程快照
- 8 jcmd:多功能命令行
1. 概述
性能诊断是软件工程师在日常工作中需要经常面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。
Java 作为最流行的编程语言之一,其应用性能诊断一直受到业界广泛关注。可能造成Javd应用出现性能问题的因素非常多,例如线程控制、磁盘读写、数据库访问、网络I/O、垃圾收集等。想要定位这些问题,一款优秀的性能诊断工具必不可少。
体会1:使用数据说明问题,使用知识分析问题,使用工具处理问题。
体会2:无监控、不调优!
简单命令行工具
在我们刚接触java学习的时候,大家肯定最先了解的两个命令就是javac,java,那么除此之外,还有没有其他的命令可以供我们使用呢?我们进入到安装jdk的bin目录,发现还有一系列辅助工具。这些辅助工具用来获取目标JVM不同方面、不同层次的信息,帮助开发人员很好地解决Java应用程序的一些疑难杂症。
2. jps:查看正在运行的Java进程
jps(Java Process Status) :
显示指定系统内所有的HotSpot虚拟机进程(查看虚拟机进程信息),可用于查询正在运行的虚拟机进程。
说明:对于本地虚拟机进程来说,进程的本地虚拟机ID与操作系统的进程ID是一致的,是唯一的。
它的基本使用语法为:jps [options] [hostid]
我们还可以通过追加参数,来打印额外的信息。
可以通过jps -help来查看对应的参数信息
options参数
-q:仅仅显示LVMID (local virtual machine id),即本地虚拟机唯一id。不显示主类的名称等
-l:输出应用程序主类的全类名或如果进程执行的是jar包,则输出jar完整路径
-m:输出虚拟机进程启动时传递给主类main()的参数
-v:列出虚拟机进程启动时的JVM参数。比如: -Xms20m -Xmx50m是启动程序指定的jvm参数。
说明:以上参数可以综合使用。
补充:
如果某Java进程关闭了默认开启的UsePerfData参数(即使用参数-XX :-UsePerfData),那么jps命令(以及下面介绍的jstat)将无法探知该java进程。
RMI注册表中注册的主机名。
如果想要远程监控主机上的java程序,需要安装jstatd。
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到IP地址欺诈攻击。
如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行jstatd服务器,而是在本地使用jstat和jps工具。
3. jstat:查看JVM统计信息
基本情况
jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
官方文档:
基本语法
它的基本使用语法为:
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
其中vmid是进程id号,也就是jps之后看到的前面的数字
查看命令相关参数:jstat -h或 jstat -help
option参数
选项option可以由以下值构成。
类装载相关的:
-class:显示ClassLoader的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
interval、count,默认interval在前面,如果只有一个,则默认是interval
-t(显示Timestamp) -h(每个多少行打印表头)
经验:
我们可以比较Java进程的启动时间以及总 GC时间(GCT列),或者两次测量的间隔时间以及总GC时间的增量,来得出GC时间占运行时间的比例。
如果该比例超过20%,则说明目前堆的压力较大;如果该比例超过90%,则说明堆里几乎没有可用空间,随时都可能抛出 OOM异常。
垃圾回收相关的:
-gc:显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。
-gccapacity:显示内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间。
-gcutil:显示内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比。
-gccause:与-gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的Gc产生的原因。
-gcnew:显示新生代GC状况
-gcnewcapacity:显示内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
-geold:显示老年代GC状况
-gcoldcapacity:显示内容与-gcold基本相同,输出主要关注使用到的最大、最小空间
-gcpermcapacity:显示永久代使用到的最大、最小空间。
JIT相关的:
-compiler:显示JIT编译器编译过的方法、耗时等信息
-printcompilation:输出已经被JIT编译的方法
-gc参数说明
C:总容量,U:已使用量
S0C、S1C:S0区、S1区总容量
S0U、S1U:S0区、S1U已使用量
EC:Eden区总容量
OC:老年代总容量
MC:元空间总容量
CCSC: 压缩类空间总量
YGC、YGCT : YoungGC的次数与时间
FGC、FGCT : FulIGC的次数与时间
GCT :总的GC时间
补充
jstat还可以用来判断是否出现内存泄漏。
第1步:在长时间运行的 Java程序中,我们可以运行jstat命令连续获取多行性能数据,并取这几行数据中 OU列(即已占用的老年代内存)的最小值。
第2步:然后,我们每隔一段较长的时间重复一次上述操作,来获得多组OU最小值。如果这些值呈上涨趋势,则说明该Java程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄漏。
4. jinfo:实时查看和修改JVM配置参数
基本情况
jinfo(Configuration Info for Java)
查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。
在很多情况下,Java应用程序不会指定所有的Java虚拟机参数。而此时,开发人员可能不知道某一个具体的Java虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了jinfo工具,开发人员可以很方便地找到ava虚拟机参数的当前值。
官方帮助文档:
基本语法
查看:
jinfo -flags 40076
参数赋值的一部分是我们自己设置的,另外一部分是系统自动优化设置的参数信息
查看参数的配置信息
修改:
jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。
但是,并非所有参数都支持动态修改。参数只有被标记为manageable的flag可以被实时修改。其实,这个修改能力是极其有限的。
修改参数示例:修改仅对当前进程有效,重启后无效
拓展
java -XX:+PrintFlagsInitial 查看所有JVM参数启动的初始值
值前面添加冒号:的是修改之后的值,没有添加的都是没有发生改变的初始值
java -XX:+PrintFlagsFinal 查看所有JVM参数的最终值
5. jmap:导出内存映像文件&内存使用情况
基本情况
jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。
开发人员可以在控制台中输入命令“jmap -help”查阅jmap工具的具体使用方式和一些标准选项配置。
官方帮助文档:
基本语法
它的基本使用语法为:
jmlp [option] <pid>
jmap [option] <executable <core>
jmap [option] [server_id@]<remote server IP or hostname>
一般来说,使用jmap指令生成dump文件的操作算得上是最常用的jmap命令之一,将堆中所有存活对象导出至一个文件之中。
Heap Dump又叫做堆存储文件,指一个Java进程在某个时间点的内存快照。Heap Dump在触发内存快照的时候会保存此刻的信息如下:
All Objects
class,fields,primitive values and references.
All classes
ClassLoader,name , super class,static fields.
Garbage collection Roots
objects defined to be reachable by the JVM.
Thread Stacks and Local Variables
The call-stacks of threads at the moment of the snapshot,and per-frameinformation about local objects
说明:
1)通常在写Heap Dump文件前会触发一次Full GC,所以heap dump文件里保存的都是FullGC后留下的对象信息。(自动)
2)由于生成dump文件比较耗时,因此大家需要耐心等待,尤其是大N存镜像生成dump文件则需要耗费更长的时间来完成。
使用1:导出内存映像文件
手动方式:
jmap -dump:format=b,file=<filename.hprof>
jmap -dump:live,format=b,file=<filename.hprof>
format=b表示生成的是标准的dump文件,用来进行格式限定
一般使用的是第二种方式,也就是生成堆中存活对象的快照,这种方式生成的dump文件更小,传输处理都更方便。
由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。
举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。
另外,如果某个线程长时间无法跑到安全点,jmap将一直等下去。与前面讲的jstat则不同,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中,而jstat只需直接读取即可。
自动方式:
当程序发生OOM退出系统时,一些瞬时信息都随着程序的终止而消失,而重现OOM问题往往比较困难或者耗时。此时若能在OOM时,自动导出dump文件就显得非常迫切。
这里介绍一种比较常用的取得堆快照文件的方法,即使用:
-XX:+HeapDumpOnOutOfMemoryError:在程序发生OOM时,导出应用程序的当前堆快照。-XX:HeapDumpPath:可以指定堆快照的保存位置。
比如:-Xmx100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\5.hprof
自动的方式是只有在发生OOM之前才会生成dump文件,手动的方式是随时可以生成dump文件。
使用2:显示堆内存相关信息
jmap -heap 进程id
jmap -histo 进程id
jmap -heap 进程id只是时间点上的堆信息,而jstat后面可以添加参数,可以指定时间动态观察数据改变情况,而图形化界面工具,例如jvisualvm等,它们可以用图表的方式动态展示出相关信息,更加直观明了
jmap -histo 进程id 输出堆中对象的同级信息,包括类、实例数量和合计容量,也是这一时刻的内存中的对象信息
6、jhat:JDK自带堆分析工具
基本情况
jhat(JVM Heap Analysis Tool):
Sun JDK提供的jhat命令有jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照)。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。
使用了jhat命令,就启动了一个http服务,端口是7000,即http://localhost:7000/,就可以在浏览器里分析。
说明:jhat命令在DK9、JDK10中已经被删除,官方建议用VisualVM代替。因为后续被废弃,所以只是了解一下。
基本语法
它的基本使用语法为:
jhat [option] [ dumpfile]
7 jstack:打印JVM中线程快照
基本情况
jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。
生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况。
官方帮助文档:
在thread dump中,要留意下面几种状态
死锁,Deadlock(重点关注)
等待资源,waiting on condition(重点关注)
等待获取监视器,Waiting on monitor entry(重点关注)
阻塞,Blocked(重点关注)
执行中,Runnable
暂停,Suspended
对象等待中,0bject.wait()或TIMED_WAITING
停止,Parked
排查程序长时间停顿,查看个线程的转态,排查重点状态。
public class ThreadDeadLock {
public static void main(String[] args) {
StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder();
new Thread() {
@Override
public void run() {
synchronized (s1) {
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2) {
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2) {
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1) {
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
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();
System.out.println("【Thread name is :" + t.getName() + "】");
for (StackTraceElement s : v) {
System.out.println("\t" + s.toString());
}
}
}
}).start();
}
}
2022-12-10 13:31:22
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):
"DestroyJavaVM" #23 prio=5 os_prio=0 tid=0x000000000311e800 nid=0x2d78 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #21 prio=5 os_prio=0 tid=0x00000000214e1000 nid=0x2370 waiting for monitor entry [0x0000000021e8e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.atguigu.springcloud.jvm.ThreadDeadLock$2.run(ThreadDeadLock.java:63)
- waiting to lock <0x000000076e2b7970> (a java.lang.StringBuilder)
- locked <0x000000076e2b79b8> (a java.lang.StringBuilder)
at java.lang.Thread.run(Thread.java:748)
"Thread-0" #20 prio=5 os_prio=0 tid=0x00000000214ea000 nid=0x293c waiting for monitor entry [0x0000000021d8f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.atguigu.springcloud.jvm.ThreadDeadLock$1.run(ThreadDeadLock.java:35)
- waiting to lock <0x000000076e2b79b8> (a java.lang.StringBuilder)
- locked <0x000000076e2b7970> (a java.lang.StringBuilder)
"Service Thread" #19 daemon prio=9 os_prio=0 tid=0x000000001eb91000 nid=0x1074 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread11" #18 daemon prio=9 os_prio=2 tid=0x000000001eb0a800 nid=0x3fc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread10" #17 daemon prio=9 os_prio=2 tid=0x000000001eb0d800 nid=0x2628 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread9" #16 daemon prio=9 os_prio=2 tid=0x000000001eb0b800 nid=0x3114 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread8" #15 daemon prio=9 os_prio=2 tid=0x000000001eb11800 nid=0x290c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread7" #14 daemon prio=9 os_prio=2 tid=0x000000001eafa000 nid=0xe0c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread6" #13 daemon prio=9 os_prio=2 tid=0x000000001eaea800 nid=0x2aa4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread5" #12 daemon prio=9 os_prio=2 tid=0x000000001eac5000 nid=0x768 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread4" #11 daemon prio=9 os_prio=2 tid=0x000000001eab4000 nid=0x2078 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001ea9f800 nid=0x311c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001ea96800 nid=0x3ac waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001ea93000 nid=0x2650 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001ea8f000 nid=0x1fb8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001ea72000 nid=0x2174 runnable [0x000000002058e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076e3c7090> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076e3c7090> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001e99f000 nid=0x303c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001e946000 nid=0x3490 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001e926800 nid=0x1e18 in Object.wait() [0x000000002028f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076e108ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076e108ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001cecd000 nid=0xdac in Object.wait() [0x000000002018e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076e106b68> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076e106b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001e904800 nid=0x20fc runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000033c8800 nid=0x15c8 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000033ca000 nid=0x29d8 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000033cb800 nid=0x2388 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000033cd000 nid=0x306c runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00000000033d0800 nid=0x1304 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00000000033d1800 nid=0x1b68 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00000000033d4800 nid=0x3714 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00000000033d6000 nid=0x226c runnable
"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x00000000033d7000 nid=0x34bc runnable
"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x00000000033d8000 nid=0x216c runnable
"GC task thread#10 (ParallelGC)" os_prio=0 tid=0x00000000033d9800 nid=0x1008 runnable
"GC task thread#11 (ParallelGC)" os_prio=0 tid=0x00000000033dc800 nid=0x2db0 runnable
"GC task thread#12 (ParallelGC)" os_prio=0 tid=0x00000000033dd800 nid=0xa00 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001ebc6000 nid=0x2654 waiting on condition
JNI global references: 33
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00000000214e4638 (object 0x000000076e2b7970, a java.lang.StringBuilder),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001ced5338 (object 0x000000076e2b79b8, a java.lang.StringBuilder),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.atguigu.springcloud.jvm.ThreadDeadLock$2.run(ThreadDeadLock.java:63)
- waiting to lock <0x000000076e2b7970> (a java.lang.StringBuilder)
- locked <0x000000076e2b79b8> (a java.lang.StringBuilder)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at com.atguigu.springcloud.jvm.ThreadDeadLock$1.run(ThreadDeadLock.java:35)
- waiting to lock <0x000000076e2b79b8> (a java.lang.StringBuilder)
- locked <0x000000076e2b7970> (a java.lang.StringBuilder)
Found 1 deadlock.
8 jcmd:多功能命令行
在DK 1.7以后,新增了一个命令行工具jcmd。
它是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。
官方帮助文档:
jcmd拥有jmap的大部分功能,并且在oracle的官方网站上也推荐使用jcmd命令代jmap命令
jcmd -l 列出所有的JVM进程
jcmd 进程号 help 针对指定的进程,列出支持的所有具体命令
jcmd 进程号 具体命令 显示指定进程的指令命令的数据
根据以上命令来替换之前的那些操作:
Thread.print 可以替换 jstack指令
GC.class_histogram 可以替换 jmap中的-histo操作
GC.heap_dump 可以替换 jmap中的-dump操作
GC.run 可以查看GC的执行情况
VM.uptime 可以查看程序的总执行时间,可以替换jstat指令中的-t操作
VM.system_properties 可以替换 jinfo -sysprops 进程id
VM.flags 可以获取JVM的配置参数信息,可以替换jinfo -flags 进程id
注:本文是学习 尚硅谷宋红康JVM全套教程(详解java虚拟机)所做笔记。