概述
性能诊断是软件工程师在日常工作中需要经常面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。
Java 作为最流行的编程语言之一,其应用性能诊断一直受到业界广泛关注。可能造成 Java 应用出现性能问题的因素非常多,例如线程控制、磁盘读写、数据库访问、网络 I/O、垃圾收集等。想要定位这些问题,一款优秀的性能诊断工具必不可少。
体会 1:使用数据说明问题,使用知识分析问题,使用工具处理问题。
体会 2:无监控、不调优!
简单命令行工具
在我们刚接触 java 学习的时候,大家肯定最先了解的两个命令就是 javac,java,那么除此之外,还有没有其他的命令可以供我们使用呢?
我们进入到安装 jdk 的 bin 目录,发现还有一系列辅助工具。这些辅助工具用来获取目标 JVM 不同方面、不同层次的信息,帮助开发人员很好地解决 Java 应用程序的一些疑难杂症。
图片 a
官方源码地址:http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/jdk.jcmd/share/classes/sun/tools
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 参数。
综合使用:
jps -l -m 等价于jps -lm
如何将信息输出到同级文件中:
语法:命令 > 文件名称
例如:jps -l > a.txt
测试:
jps -lmv > /Users/study_book/jvm/代码/JVMDemo/logs/param.log
返回:
14633 org.jetbrains.jps.cmdline.Launcher /Applications/IntelliJ IDEA.app/Contents/lib/netty-common-4.1.52.Final.jar:/Applications/IntelliJ IDEA.app/Contents/lib/netty-resolver-4.1.52.Final.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/java/lib/javac2.jar:/Applications/IntelliJ IDEA.app/Contents/lib/httpclient-4.5.12.jar:/Applications/IntelliJ IDEA.app/Contents/lib/plexus-component-annotations-1.7.1.jar:/Applications/IntelliJ IDEA.app/Contents/lib/maven-resolver-spi-1.3.3.jar:/Applications/IntelliJ IDEA.app/Contents/lib/util.jar:/Applications/IntelliJ IDEA.app/Contents/lib/platform-api.jar:/Applications/IntelliJ IDEA.app/Contents/lib/qdox-2.0-M10.jar:/Applications/IntelliJ IDEA.app/Contents/lib/asm-all-9.0.jar:/Applications/IntelliJ IDEA.app/Contents/lib/commons-lang3-3.10.jar:/Applications/IntelliJ IDEA.app/Contents/lib/jna.jar:/Applications/IntelliJ IDEA.app/Contents/lib/trove4j.jar:/Applications/IntelliJ IDEA.app/Contents/lib/nanoxml-2.2.3.jar:/Applications/IntelliJ IDEA.app/Contents/lib/maven-resolver-api -Xmx700m -Djava.awt.headless=true -Djava.endorsed.dirs="" -Djdt.compiler.useSingleThread=true -Dpreload.project.path=/Users/lizhengguang/study_book/jvm/代码/JVMDemo -Dpreload.config.path=/Users/lizhengguang/Library/Application Support/JetBrains/IntelliJIdea2020.3/options -Dcompile.parallel=false -Drebuild.on.dependency.change=true -Djava.net.preferIPv4Stack=true -Dio.netty.initialSeedUniquifier=2579944760633116663 -Dfile.encoding=UTF-8 -Duser.language=zh -Duser.country=CN -Didea.paths.selector=IntelliJIdea2020.3 -Didea.home.path=/Applications/IntelliJ IDEA.app/Contents -Didea.config.path=/Users/lizhengguang/Library/Application Support/JetBrains/IntelliJIdea2020.3 -Didea.plugins.path=/Users/lizhengguang/Library/Application Support/JetBrains/IntelliJIdea2020.3/plugins -Djps.log.dir=/Users/lizhengguang/Library/Logs/JetBrains/IntelliJIdea2020.3/build-log -Djps.fallback.jdk.home=/Applications/IntelliJ IDEA.app/Contents/jbr/Contents/Home -Djps.fallback.jdk.version=11.0.10 -Dio.netty.noUnsafe=true -Djava.io.tmpdir 14665 sun.tools.jps.Jps -lmv -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home -Xms8m 14634 com.atguigu.java.GCUseTest 1,2,3,4,5 -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53701,suspend=y,server=n -Xms2048m -Xmx2048m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -Xloggc:./logs/gc.log -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -XX:ConcGCThreads=2 -XX:InitiatingHeapOccupancyPercent=45 -XX:G1HeapWastePercent=11 -javaagent:/Users/lizhengguang/Library/Caches/JetBrains/IntelliJIdea2020.3/captureAgent/debugger-agent.jar -Dfile.encoding=UTF-8 427 -Xms1014m -Xmx2014m -XX:ReservedCodeCacheSize=240m -XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -ea -XX:CICompilerCount=2 -Dsun.io.useCanonPrefixCache=false -Djava.net.preferIPv4Stack=true -Djdk.http.auth.tunneling.disabledSchemes="" -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -Djdk.attach.allowAttachSelf=true -Dkotlinx.coroutines.debug=off -Djdk.module.illegalAccess.silent=true -XX:+UseCompressedOops -Dfile.encoding=UTF-8 -XX:ErrorFile=/Users/lizhengguang/java_error_in_idea_%p.log -XX:HeapDumpPath=/Users/lizhengguang/java_error_in_idea.hprof -javaagent:/Users/lizhengguang/.jetbrains/jetbrains-agent-v3.0.0.jar -Djb.vmOptionsFile=/Users/lizhengguang/Library/Application Support/JetBrains/IntelliJIdea2020.3/idea.vmoptions -Didea.paths.selector=IntelliJIdea2020.3 -Didea.executable=idea -Didea.home.path=/Applications/IntelliJ IDEA.app/Contents -Didea.vendor.name=JetBrains
hostid参数:
RMI 注册表中注册的主机名。如果想要远程监控主机上的 java 程序,需要安装 jstatd。
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到 IP 地址欺诈攻击。
如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行 jstatd 服务器,而是在本地使用 jstat 和 jps 工具。
3. jstat:查看 JVM 统计信息
jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据。在没有 GUI 图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
基本使用语法为:
查看命令相关参数:jstat -h 或 jstat -help
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
解释: <option>由-options选项报告的选项
-t 参数: 可以在输出信息前加上一个 Timestamp 列,显示程序的运行时间。单位:秒 <vmid>虚拟机标识符(进程 id)。vmid采用以下形式: <lvmid>[@<hostname>[:<port>]:其中<lvmid>是目标的本地vm标识符Java虚拟机(进程 id),通常是进程id<主机名>是运行目标Java虚拟机的主机的名称;和<port>是服务器上RMI注册表的端口号目标主机。请参阅jvmstat文档以获取更完整的信息 虚拟机标识符的说明。 -h<lines> :标题行之间的样本数,可以在周期性数据输出时,输出多少行数据后输出一个表头信息。 <interval> :采样间隔。用于指定输出统计数据的周期,单位为ms/s,默认是 ms。即:查询间隔;<count> :用于指定查询的总次数,使用该命令必须指定 interval ,否者会将他默认认为是interval。
其中 vmid 是进程 id 号,也就是 jps 之后看到的前面的号码,如下:
option 参数
选项 option 可以由以下值构成。
类装载相关的:
-class:显示 ClassLoader 的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
垃圾回收相关的:
-gcpermcapacity:显示永久代使用到的最大、最小空间。
-gc : 显示与 GC 相关的堆信息。包括 Eden 区、两个 Survivor
区、老年代、永久代等的容量、已用空间、GC 时间合计等信息。
-gccapacity : 显示内容与-gc 基本相同,但输出主要关注 Java
堆各个区域使用到的最大、最小空间。
-gccause : 与-gcutil 功能一样,但是会额外输出导致最后一次或当前正在发生的 GC 产生的原因。
-gcmetacapacity
-gcnew : 显示新生代 GC 状况
-gcnewcapacity : 显示内容与-gcnew
基本相同,输出主要关注使用到的最大、最小空间
-gcold : 显示老年代 GC 状况
-gcoldcapacity :
显示内容与-gcold 基本相同,输出主要关注使用到的最大、最小空间
-gcutil : 显示内容与-gc 基本相同,但输出主要关注已使用空间占总空间的百分比。
JIT 相关的:
-compiler : 显示 JIT 编译器编译过的方法、耗时等信息
-printcompilation : 输出已经被 JIT 编译的方法
1、举例:
jstat -gcnew -t -h3 14683 1000 10
解释:
其中h3中的3代表每隔3个分隔一次,14628 代表类的进程id,1000 代表每隔1000毫秒打印一次,10 代表一共打印10次,如下所示:
2、-gc举例: jstat -gc 13152 ,其中13152代表类的进程id,执行结果如下所示:
其中S0C代表幸存者0区的总容量,S1C代表幸存者1区的总容量,S0U代表幸存者0区使用的容量,S1U代表幸存者1区使用的容量,EC代表伊甸园区的总容量,EU代表伊甸园区使用的总容量,OC代表老年代的总容量,OU代表老年代已经使用的容量,MC代表方法区的总容量,MU代表方法区的总容量,CCSC代表压缩类的总容量,CCSU代表压缩类使用的容量,YGC代表年轻代垃圾回收的次数,YGCT年轻代进行垃圾回收需要的时间,FGC代表代表Full GC的次数,FGCT代表Full GC的时间,GCT代表垃圾回收的总时间
3、-gccapacity举例: jstat -gccapacity 13152 ,其中13152代表类的进程id,执行结果如下:
其中S0C代表幸存者0区的容量,S1C代表幸存者1区的容量,EC代表伊甸园区的容量,CCSC代表压缩类的容量,YGC代表年轻代垃圾回收的时间,FGC代表Full GC垃圾回收的时间
4、-gcutil举例: jstat -gcutil 13152 ,其中13152代表类的进程id,执行结果如下所示:
以上是各区域占比以及垃圾回收的情况,S0代表幸存者0区,S1代表幸存者1区,E代表伊甸园区,O代表老年代,M代表方法区,CCS代表压缩类,以上这些值都是占比情况,YGC代表年轻代垃圾回收的次数,YGCT年轻代进行垃圾回收需要的时间,FGC代表代表Full GC的次数,FGCT代表Full GC的时间,GCT代表垃圾回收的总时间
5、-gccause举例: jstat -gccause 13152 ,其中13152代表类的进程id,执行结果如下:
以上是各区域占比以及垃圾回收的情况,还有触发垃圾回收的原因解释,S0代表幸存者0区,S1代表幸存者1区,E代表伊甸园区,O代表老年代,M代表方法区,CCS代表压缩类,以上这些值都是占比情况,YGC代表年轻代垃圾回收的次数,YGCT年轻代进行垃圾回收需要的时间,FGC代表代表Full GC的次数,FGCT代表Full GC的时间,GCT代表垃圾回收的总时间,LGCC和GCC代表垃圾回收的原因
补充:
jstat 还可以用来判断是否出现内存泄漏。
第 1 步:在长时间运行的 Java 程序中,我们可以运行 jstat 命令连续获取多行性能数据,并取这几行数据中 OU 列(即已占用的老年代内存)的最小值。
第 2 步:然后,我们每隔一段较长的时间重复一次上述操作,来获得多组 OU 最小值。如果这些值呈上涨趋势,则说明该 Java 程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄漏。
我们执行jstat -gc -t 15013 1s 10 ,这代表1秒打印出1行,一共10行,-t代表打印出Timestamp总运行时间,结果如下所示:
表头 | 含义(字节) |
---|---|
EC | Eden 区的大小 |
EU | Eden 区已使用的大小 |
S0C | 幸存者 0 区的大小 |
S1C | 幸存者 1 区的大小 |
S0U | 幸存者 0 区已使用的大小 |
S1U | 幸存者 1 区已使用的大小 |
MC | 元空间的大小 |
MU | 元空间已使用的大小 |
OC | 老年代的大小 |
OU | 老年代已使用的大小 |
CCSC | 压缩类空间的大小 |
CCSU | 压缩类空间已使用的大小 |
YGC | 从应用程序启动到采样时 young gc 的次数 |
YGCT | 从应用程序启动到采样时 young gc 消耗时间(秒) |
FGC | 从应用程序启动到采样时 full gc 的次数 |
FGCT | 从应用程序启动到采样时的 full gc 的消耗时间(秒) |
GCT | 从应用程序启动到采样时 gc 的总时间 |
4. jinfo:实时查看和修改 JVM 配置参数
jinfo(Configuration Info for Java):查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。在很多情况卡,Java 应用程序不会指定所有的 Java 虚拟机参数。而此时,开发人员可能不知道某一个具体的 Java 虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了 jinfo 工具,开发人员可以很方便地找到 Java 虚拟机参数的当前值。
基本使用语法为:
jinfo [options] pid
说明:java 进程 ID 必须要加上
选项 | 选项说明 |
---|---|
no option | 输出全部的参数和系统属性 |
-flag name | 输出对应名称的参数 |
-flag [+-]name | 开启或者关闭对应名称的参数 只有被标记为 manageable 的参数才可以被动态修改 |
-flag name=value | 设定对应名称的参数 |
-flags | 输出全部的参数 |
-sysprops | 输出系统属性· |
举例:
jinfo -sysprops 进程 id
可以查看由System.getProperties()取得的参数
jinfo -flags 进程 id
查看曾经赋过值的一些参数
jinfo -flag 参数名称 进程id
查看某个java进程的具体参数信息
针对boolean类型
jinfo -flag [+|-]参数名称 进程id
针对非boolean类型
jinfo -flag 参数名称=参数值 进程id
拓展
java -XX:+PrintFlagsInitial
查看所有JVM参数启动的初始值
java -XX:+PrintFlagsFinal
查看所有JVM参数的最终值
值前面添加冒号: 的是修改之后的值,没有添加的都是没有发生改变的初始值
java -XX:+PrintCommandLineFlags
查看那些已经被用户或者JVM设置过的详细的XX参数的名称和值
5. jmap:导出内存映像文件&内存使用情况
jmap(JVM Memory Map):作用一方面是获取 dump 文件(堆转储快照文件,二进制文件),它还可以获取目标 Java 进程的内存相关信息,包括 Java 堆各区域的使用情况、堆中对象的统计信息、类加载信息等。开发人员可以在控制台中输入命令“jmap -help”查阅 jmap 工具的具体使用方式和一些标准选项配置。
官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jmap.html
基本使用语法为:
- jmap [option] <pid>
- jmap [option] <executable <core> 代表可执行的代码,比如使用> 文件名称 来指定生成的dump文件的生成位置
- jmap [option] [server_id@] <remote server IP or hostname>
选项 | 作用 |
---|---|
-dump | 生成 dump 文件(Java 堆转储快照),-dump:live 只保存堆中的存活对象 |
-heap | 输出整个堆空间的详细信息,包括 GC 的使用、堆配置信息,以及内存的使用信息等 |
-histo | 输出堆空间中对象的统计信息,包括类、实例数量和合计容量,-histo:live 只统计堆中的存活对象 |
-J <flag> | 传递参数给 jmap 启动的 jvm |
-finalizerinfo | 显示在 F-Queue 中等待 Finalizer 线程执行 finalize 方法的对象,仅 linux/solaris 平台有效 |
-permstat | 以 ClassLoader 为统计口径输出永久代的内存状态信息,仅 linux/solaris 平台有效 |
-F | 当虚拟机进程对-dump 选项没有任何响应时,强制执行生成 dump 文件,仅 linux/solaris 平台有效 |
说明:这些参数和 linux 下输入显示的命令多少会有不同,包括也受 jdk 版本的影响。
使用1:导出内存映像文件
说明:
1、通常在写 Heap Dump 文件前会触发一次 Full GC,所有 heap dump 文件保存的都是 FullGC 后留下的对象信息。
2、由于生成 dump 文件比较耗时,因此大家需要等待,尤其是大内存镜像生成 dump 文件则需要更多的时间来完成。
注意:
- 对于以上说明中的第1点是只有自动生成 Dump 文件前才会触发 Full GC,手动触发生成 Dump 不会触发 FullGC
- 使用手动方式生成dump文件,一般指令执行之后就会生成,不用等到快出现OOM的时候
- 使用自动方式生成dump文件,当出现OOM之前先生成dump文件
- 如果使用手动方式,一般使用 手动的方式中的方式二 (只生成存活对象的快照),毕竟生成堆中存活对象的dump文件是比较小的,便于传输和分析
手动的方式
方式一:
命令 1:
jmap -dump:format=b,file=<filename.hprof> <pid>
说明:
1. <filename.hprof> 中的filename 是文件名称,而.hprof 是后缀名, <***>代表该值可以省略<>,当然后面的<pid>是进程id,需要通过jps查询出来
2. format=b表示生成的是标准的dump文件,用来进行格式限定
3. 其中file= 后面的是生成的dump文件 地址,最后的 pid 是进程id ,可以通过jps 查看
具体例子如下:
生成堆中所有对象的快照:
jmap -dump:format=b,file=/Users/lizhengguang/study_book/jvm/代码/JVMDemo/logs/1.hprof 15883
方式二:
命令 2: jmap -dump:live,format=b,file=<filename.hprof> <pid>
生成堆中存活对象的快照:
jmap -dump:live,format=b,file=/Users/lizhengguang/study_book/jvm/代码/JVMDemo/logs/2.hprof 15883
返回信息如下:
一般使用的是第二种方式,也就是生成堆中存活对象的快照,毕竟这种方式生成的dump文件更小,我们传输处理都更方便
由于 jmap将访问堆中的所有对象,为了保证此过程不被其他的线程干扰,jmap 需要借助安全点机制,让所有线程停留在不改变堆中数据的状态,也就是说,由于 jmap 导出的堆内存快照一定是安全点位置的,这可能导致基于该内存快照的分析结果存在误差。
举个例子,假设在编译生成的机器吗中,某些对象的生命周期在两个安全点之间,那个:live选项将无法探知到这些对象。
另外,如果某个线程长时间无法跑到安全点,jmap 将一直等下去,与前面讲的 jstat 则不同,垃圾回收器会主动将 jstat 所需要的摘要数据保存到固定位置中,而 jstat 只需要直接读取即可。
自动的方式
当程序发生了 OOM 退出系统时,一些瞬时信息都随着程序的终止而消失,而重现 OOM 问题往往比较困难。此时若能在 OOM 时,自动导出 dump 文件就显得很迫切
这里介绍一种比较常用的取得内存快照文件的方法,即使用:
-XX:+HeapDumpOnOutOfMemoryError : 在程序发生 OOM 时,导出应用程序的当前堆快照。
-XX:HeapDumpPath=<filename.hprof> : 可以指定堆的保存位置
比如:
参数:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/lizhengguang/study_book/jvm/代码/JVMDemo/heapdump/1.hprof
具体使用如下:
生产环境:请放在 jvm 的启动参数中
测试环境在 VM options 中写入参数,触发 oom 即可;
使用2:显示堆内存相关信息
jmap -heap 进程id
1. jmap -heap 进程id 只是时间点上的堆信息,而jstat 后面可以添加参数,可以指定时间动态观察数据改变情况,而图形化界面工具 ,例如jvisualvm等,它们可以用图表的方式动态展示出相关信息,更加直观明了
2. 例子参数如下:
jmap -heap 17969
jmap -histo 进程id
1. 输出堆中对象的同级信息,包括类、实例数量和合计容量,也是这一时刻的内存中的对象信息
2. 参数例子如下:
jmap -histo 17988
使用3:其他作用
jmap -permstat 进程id
查看系统的ClassLoader信息
jmap -finalizerinfo
查看堆积在finalizer队列中的对象
这两个指令仅linux/solaris平台有效 ,所以无法在windows操作平台上演示,并且使用比较小众,不在多说
小结:
由于 jmap 将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap 需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由 jmap 导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。
举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live 选项将无法探知到这些对象。
另外,如果某个线程长时间无法跑到安全点,jmap 将一直等下去。与前面讲的 jstat 则不同,垃圾回收器会主动将 jstat 所需要的摘要数据保存至固定位置之中,而 jstat 只需直接读取即可。
6-jhat:JDK自带堆分析工具(了解)
基本情况
jhat命令在jdk9及其之后就被移除了,官方建议使用jvisualvm代替jhat,所以该指令只需简单了解一下即可
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 代替。
基本语法
其中dumpfile代表dump文件的地址以及名称,例如:
options参数
7-jstack:打印JVM中线程快照
基本情况
jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。
生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用 jstack 显示各个线程调用的堆栈情况。
官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jstack.html
在 thread dump 中,要留意下面几种状态
- 死锁,Deadlock(重点关注)
- 等待资源,Waiting on condition(重点关注)
- 等待获取监视器,Waiting on monitor entry(重点关注)
- 阻塞,Blocked(重点关注)
- 执行中,Runnable
- 暂停,Suspended
- 对象等待中,Object.wait() 或 TIMED_WAITING
- 停止,Parked
option 参数 | 作用 |
---|---|
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-l | 除堆栈外,显示关于锁的附加信息 |
-m | 如果调用本地方法的话,可以显示 C/C++的堆栈 |
基本语法
jstack <options> <pid>
总结:
如果程序出现等待问题,可以使用该指令去查看问题所在,结果中也会提示你问题所在
8. jcmd:多功能命令行
在 JDK 1.7 以后,新增了一个命令行工具 jcmd。它是一个多功能的工具,可以用来实现前面除了 jstat 之外所有命令的功能。比如:用它来导出堆、内存使用、查看 Java 进程、导出线程信息、执行 GC、JVM 运行时间等。
官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jcmd.html
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 的配置参数信息
例如:
jcmd 18221 VM.flags
9. jstatd:远程主机信息收集
之前的指令只涉及到监控本机的 Java 应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如 jps、jstat)。为了启用远程监控,则需要配合使用 jstatd 工具。命令 jstatd 是一个 RMI 服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。jstatd 服务器将本机的 Java 应用程序信息传递到远程计算机。