JVM 问题排查过程中的常用命令
JVM 问题排查过程中的常用命令
jps(看进程号)
jps [ options ]
[ hostid ]
列出指定主机上的所有虚拟机(一个 Java 应用就是一个虚拟机,一个虚拟机就是一个进程),该命令仅限于报告它具有访问权限的 jvm 的信息。其中,hostid 可以包含可选组件,表示通信协议、端口号和其他具体实现数据。
如果 jps 命令在没有指定 hostid 的情况下运行,那么它将在本地主机上搜索已安装的 jvm。如果以 hostid 开头,则使用指定的协议和端口在指定的主机上搜索 jvm,前提是 jstatd 进程在目标主机上运行。jps 命令报告在目标系统上发现的每个 JVM 的标识符 (或 lvmid)。lvmid 通常是操作系统中 JVM 进程的进程 ID,但不一定是这样。在没有选项的情况下,jps 列出了每个 Java 应用程序的 lvmid,后面跟着应用程序类名或 jar 文件名的简短形式。类名或 JAR 文件名的缩写形式省略了类的包信息或 JAR 文件的路径信息。
jps 命令使用 Java 启动器(Java launcher)查找传递给主方法的类名和参数。如果目标 JVM 是用自定义启动器启动的,那么类或 JAR 文件名和主方法的参数都不可用。在这种情况下,jps 命令为类名或 JAR 文件名以及 main 方法的参数输出字符串 Unknown。
选项(options):以下测试结果都是在一个只运行了一个 tomcat 的服务器上得出的
- -l 显示应用程序主类的完整包名或应用程序 JAR 文件的完整路径名。
Tomcat 的虚拟机是通过 Bootstrap 类启动的。 - -m 显示传递给 main 方法的参数。对于嵌入式 jvm,输出可能为空。
很有意思的是,jps 也被显示出来了,而且 jps 命令的参数确实就只是 -m - -v 显示传递给 JVM 的参数。
常用,我们经常通过这个来看 Tomcat 的启动参数
- 主机 ID:
主机 ID(hostid)是表示目标主机的字符串。hostid 字符串的语法与 URI 的语法相对应
[protocol:][[//]hostname][:port][/servername]
剩下的看官方文档
jinfo(看配置)
jinfo [ option ] pid
jinfo [ option ] executable core
jinfo [ option ] [ servier-id ] remote-hostname-or-IP
其中:
- pid:要打印配置信息的进程 ID。该进程必须是 Java 流程。要获取机器上运行的 Java 进程列表,请使用 JVM问题排查过程中的常用命令#jps(看进程号)。
- executable:用的不多
- core:用的不多
- remote-hostname-or-IP:用的不多
- server-id:用的不多
jinfo 命令用于打印指定 Java 进程或核心文件或者远程调试服务器的 Java 配置信息。配置信息包括 Java 系统属性(Java system properties)和 Java 虚拟机 (JVM) 命令行标志(Java Virtual Machine command-line flags)。如果指定的进程运行在 64 位 JVM 上,那么您可能需要指定-J-d64
选项,例如:jinfo -J-d64 -sysprops pid
。
该工具可能在 JDK 的未来版本中不可用。在没有dbgen.dll
的 Windows 系统中,必须安装 Windows 调试工具才能让这些工具工作。PATH 环境变量应该包含目标进程所使用的jvm.dll
的位置,或者生成崩溃转储文件的位置。例如: 设置PATH=%JDK HOME%\jre\bin\client;%PATH%
。
配置项:
- 没有配置项
打印命令行标志和系统属性名称 - 值对。
Java 系统属性真的好多啊(从 Java system properties:开始),相比之下 VM 标志(从 VM Flags:开始))和命令行(从 Command line:开始)反而很少,
我发现在系统属性中可以看到 jdk 的地址,也就是说,通过 jps 查看所有的 Java 虚拟机,然后通过 jinfo 查看系统属性,在 sun.boot.class.path 属性中即可看到 jdk 的地址 - -flag name
打印指定命令行标志的名称和值。
flag [+|-]name
启用或禁用指定的 Boolean 命令行标志。- -flag name=value
将指定的命令行标志设置为指定的值。 - -flags
打印传递给 JVM 的标志和命令行。
- -sysprops
以名 - 值对的形式打印 Java 系统属性。
剩下的看官方文档
jmap(看堆内存)
jmap [ options ] pid
jmap [ options ] executable core
jmap [ options ] [ pid ] server-id@ ] remote-hostname-or-IP
其中
- pid:要打印内存映射的进程 ID。该进程必须是 Java 进程。要获取机器上运行的 Java 进程列表,请使用 JVM问题排查过程中的常用命令#jps(看进程号)。
- executable:用的不多
- core:用的不多
- remote-hostname-or-IP:用的不多
- server-id:用的不多
jmap 命令打印指定进程或者核心文件或远程调试服务器的共享对象内存映射或堆内存的详细信息。如果指定的进程运行在 64 位 Java Virtual Machine (JVM) 上,那么您可能需要指定-J-d64
选项,例如:jmap -J-d64 -heap pid
。
该工具可能在 JDK 的未来版本中不可用。在没有dbgen.dll
的 Windows 系统中,必须安装 Windows 调试工具才能让这些工具工作。PATH 环境变量应该包含目标进程所使用的 jvm.dll 的位置,或者生成崩溃转储文件的位置。例如: 设置PATH=%JDK HOME%\jre\bin\client;%PATH%
。
配置项:
- 没有配置项
当不使用选项时,jmap 命令打印共享对象映射。对于加载到目标 JVM 中的每个共享对象,将打印出开始地址、映射的大小和共享对象文件的完整路径。此行为类似于 Oracle Solaris pmap 工具。
-dump:[live,] format=b, file=filename
常用
将 Java 堆以 hprof(Heap Profile 的缩写) 二进制格式转储到文件名。子选项live是可选的,但是当指定时,只转储堆中的活动对象。要浏览堆转储出来的文件,可以使用 JVM问题排查过程中的常用命令#jhat(分析内存文件用页面展示) 命令读取生成的文件。
一般,我们都会带上 live,如果不带上,打印出来的 hprof 文件会大很多
我们可以把文件 down 到本地,然后用 VisualVM 进行分析:
- -finalizerinfo
打印关于正在等待结束的对象的信息。会调用 Java 代码中的 Object 的 finalize 方法,回看《Java 核心技术》卷一的 4.6.8 小节
- -heap 常用
Prints a heap summary of the garbage collection used, the head configuration, and generation-wise heap usage。
此外,还会打印出内部的 String 对象的数量和大小。
从结果看,输出的都是跟堆相关的信息,跟 GC 相关的信息很少啊。 -histo[:live]
常用
打印堆的直方图。对于每个 Java 类,将打印对象数量、内存大小 (以字节为单位) 和完全限定类名。JVM 内部类名以星号 (*
) 前缀打印。如果指定了活动子选项,则只计算活动对象。
太多了,只看前 20 个试试,注意,他是按照类所有的实例占用的字节数倒叙排列的
jmap -histo:live 29107 | head -n 20
其中以[
开头的都是数组,[C
就是 char 数组,[I
就是 int 数组,实际上就对应着 VisualVM 分析 dump 文件的那个截图中的类的那个 tab 的信息。
排在前 20 名的都是一些熟面孔,String 类,Class 类,反射,多线程,Map 集合节点。- -clstats
打印 Java 堆中,类加载器相关的统计数据的。对于每个类加载器,它的名称、活动程度、地址、父加载器以及装入的类的数量和大小都被打印出来。
这个过程有点耗时
...
省略
...
剩下的看官方文档 jmap.txt
jhat(分析内存文件用页面展示)
jhat [ options ] heap-dump-file
其中:
- heap-dump-file:要浏览的 Java 二进制堆转储文件(Java binary heap dump file )。对于包含多个堆转储的转储文件,您可以通过在文件名后面附加
#<number>
来指定文件中的转储,例如,myfile.hprof#3
。
jhat 命令解析 Java 堆转储文件(Java heap dump file)并启动 web 服务器。jhat 命令允许您使用您最喜欢的 web 浏览器浏览堆转储。jhat 命令支持预先设计的查询,例如显示一个已知类 MyClass 的所有实例,以及对象查询语言 (OQL)。OQL 类似于 SQL,除了查询堆转储(heap dump )。OQL 的帮助可以从 jhat 命令显示 OQL 帮助页面获得,使用默认端口的话,OQL 帮助可在http://localhost:7000/oqlhelp/
中找到
有几种方法可以生成 Java 堆转储文件:
- 使用
jmap -dump
选项在运行时获取堆转储。看到 JVM问题排查过程中的常用命令#jmap(看堆内存)。(用的比较多) - 使用 jconsole 选项可以在运行时通过 HotSpotDiagnosticMXBean 获取堆转储。看到 jconsole(1) 和 HotSpotDiagnosticMXBean 接口描述在
http://docs.oracle.com/javase/8/docs/jre/api/management/extension/com/sun/management/HotSpotDiagnosticMXBean.html
- 当指定 Java 虚拟机 (JVM) 选项
-XX:+HeapDumpOnOutOfMemoryError
,在抛出 OutOfMemoryError 时,生成堆转储。(用的比较多) - 使用 hprof 命令。请参阅 HPROF:堆/ CPU 分析工具 http://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html.
选项: - -stack false|true
关闭跟踪对象分配调用堆栈。如果在堆转储中无法获得分配站点信息,则必须将该标志设置为 false。默认为 true。
Web 服务的默认端口是 7000,服务器的 7000 必须打开了,这样才能在外部访问到,
开启接口
刷新防火墙
访问:http://172.16.20.143:7000/
可以很方便的在网页中根据链接跳转。
拉到最下面还可以看到堆直方图,即jmap -histo
点进去看到的也是同样的内容
- -refs false|true
关闭对对象的引用跟踪。默认是 true。默认情况下,为堆中的所有对象计算反向指针(back pointers),反向指针是指向指定对象 (如引用或传入引用) 的对象。
类信息的没有了
- -port port-number
设置 jhat HTTP 服务器的端口。默认是 7000。 - -exclude exclude-file
指定一个文件,该文件列出应从可达对象查询(reachable objects query)中排除的数据成员。例如,如果文件中列出 java.lang.String.value,然后,无论何时计算从特定对象 o 可到达的对象列表,涉及 java.lang.String.value 字段的引用路径都不会被考虑。
- -baseline exclude-file
指定基线堆转储 (baseline heap dump)。两个堆转储中具有相同对象 ID 的对象被标记为不是新的。其他对象被标记为新的。这对于比较两个不同的堆转储非常有用。 - -debug int
设置此工具的调试级别。0 级别表示没有调试输出。为更详细的模式设置更高的值。 - -Jflag
将 flag 传递给正在运行 jhat 命令的 Java 虚拟机。例如,使用 -J-Xmx512m 来使用最大堆大小 512mb。
剩下的看官方文档
jstack(看栈帧)
jstack [ options ] pid
jstack [ options ] executable core
jstack [ options ] [ server-id@ ] remote-hostname-or-IP
其中,
- pid:打印堆栈跟踪(stack trace)的进程 ID。该进程必须是 Java 进程。要获取机器上运行的 Java 进程列表,请使用 JVM问题排查过程中的常用命令#jps(看进程号)。
- executable:用的不多
- core:用的不多
- remote-hostname-or-IP:用的不多
- server-id:用的不多
jstack 命令打印指定 Java 进程、core 文件,或远程调试服务器的 Java 线程的 Java 堆栈跟踪信息。对于每个 Java 帧(frame),完整的类名、方法名、字节码索引 (BCI) 和行号 (如果可用) 将被打印出来。使用 -m 选项,jstack 命令使用程序计数器 (PC) 打印所有线程的 Java 和 native 帧(native 帧就是 native 方法即 C++ 方法的调用栈)。为每个 native 帧,最接近 PC 的 native 符号,当可用时,被打印。c++ 的名字是无法显示的(这里原文有但此错误,我猜是这个意思)。为了获取 c++ 名称,这个命令的输出可以通过管道传递到 c++filt。如果指定的进程运行在 64 位 Java 虚拟机上,则可能需要指定 -j-d64 选项,例如:jstack -J-d64 -m pid
。
该工具可能在 JDK 的未来版本中不可用。在没有 dbgen.dll 的 Windows 系统中,必须安装 Windows 调试工具才能让这些工具工作。PATH 环境变量应该包含目标进程所使用的 jvm.dll 的位置,或者生成崩溃转储文件的位置。例如: 设置PATH=%JDK HOME%\jre\bin\client;%PATH%
。
选项:
- 什么选项都不带
显示所有的栈帧
- -l
长清单。打印关于锁的额外信息,比如拥有的java.util.concurrent
ownable 同步器的列表。
See the AbstractOwnableSynchronizer class description at http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/AbstractOwnableSynchronizer.html
会显示额外的锁相关的栈帧
大名鼎鼎的 AbstractQueuedSynchronizer 实际上就继承了 AbstractOwnableSynchronizer,也就是说通过一个同步器对象可以拿到锁当前被谁持有,都有都可以拿到当前持有锁
懒得复制具体内容了 - -F
当jstack [-l] pid
没有响应时强制一个堆栈转储。 - -m
打印包含 Java 和本机C/ c++
帧的混合模式堆栈跟踪。
会打印更多 C / c++ 相关的帧,看得更深
剩下的看官方文档
jstat (JVM 统计数据,常用于看 GC 信息)
jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]
其中:
- generalOption: 一个通用命令行选项 -help 或 -options。
- outputOptions: 一个或多个输出选项,包括单个 statOption,以及任意的 -t、-h 和 -J 选项。
vmid: 虚拟机标识符,它是一个指示目标 JVM 的字符串。一般语法如下所示
[protocol:][//]lvmid[@hostname[:port]/servername]
vmid 字符串的语法对应于 URI 的语法。vmid 字符串可以是一个简单的整数 (表示本地 JVM),也可以是一个更复杂的结构 (指定通信协议、端口号和其他特定于实现的值)。参见虚拟机标识符。 interval [s|ms]
: 采样间隔,单位为指定的秒 (s) 或毫秒 (ms)。默认单位为毫秒。必须是正整数。如果指定了该参数,jstat 命令将在每个时间间隔生成它的输出。 如果不带,只会输出一行。 常用- count: 显示采样数据的数量。默认值是 infinity,这将导致 jstat 命令显示统计信息,直到目标 JVM 终止或 jstat 命令终止。该值必须为正整数。 如果指定了 interval 但是没有指定 count 则会一直按照 interval 输出。常用
jstat 命令显示一个已测试的 Java HotSpot VM 的性能统计数据。目标 JVM 由其虚拟机标识符或 vmid 选项标识。
通用选项:
- -help
- -options
输出选项:
输出选项决定 jstat 命令输出的内容和格式,由单个 statOption 和任何其他输出选项 (-h、-t 和 -J) 组成。statOption 必须在其他输出选项的前面。
输出被格式化为一个表,列用空格分隔。带有标题的标题行用于描述列。
使用 -h 选项设置标题行显示的频率。所有的列标头名称在不同选项之间是一致的。即:如果两个选项提供具有相同名称的列,那么这两个列的数据源是相同的。
使用 -t 选项显示时间戳列,将 Timestamp 标记为输出的第一列。Timestamp 列包含自目标 JVM 启动以来经过的时间 (以秒为单位)。时间戳的精度取决于各种因素,并且由于在高负载系统上延迟的线程调度而变化。
使用 interval 和 count 参数分别确定 jstat 命令显示输出的频率和次数。 - -statOption:
- class: Displays statistics about the behavior of the class loader.
- compiler: Displays statistics about the behavior of the Java HotSpot VM Just-in-Time compiler.
- gc: Displays statistics about the behavior of the garbage collected heap.
- gccapacity: Displays statistics about the capacities of the generations and their
- corresponding spaces.
- gccause: Displays a summary about garbage collection statistics (same as -gcutil), with the
- cause of the last and current (when applicable) garbage collection events.
- gcnew: Displays statistics of the behavior of the new generation.
- gcnewcapacity: Displays statistics about the sizes of the new generations and its
- corresponding spaces.
- gcold: Displays statistics about the behavior of the old generation and metaspace statistics.
- gcoldcapacity: Displays statistics about the sizes of the old generation.
- gcmetacapacity: Displays statistics about the sizes of the metaspace.
- gcutil: 显示有关垃圾收集统计信息的摘要。 常用
用的最多的就是这个选项,
输出项:
其他选项的输出项可以看 g 官方文档的 Stat Options and Output 部分。
分析数据的例子:
- printcompilation: Displays Java HotSpot VM compilation method statistics.
- -h n
每 n 个样本 (输出行) 显示一个标题行,其中 n 是一个正整数。默认值是 0,它显示第一行数据的列标题。
- -t
将时间戳列显示为输出的第一列。时间戳是从目标 JVM 的开始时间开始的时间。
jstatd(RMI 远程方法调用应用程序)
jstatd [ options ]
jstatd 命令是一个RMI 服务器应用程序,它监视被检测的 Java HotSpot 虚拟机的创建和终止,并提供一个接口,使远程监视工具能够连接到运行在本地主机上的 jvm。(这里的主机指的是被远程监控的主机)
jstatd 服务器需要在本地主机上进行 RMI 注册。jstatd 服务器尝试通过默认端口连接到 RMI 注册表上,或者使用 -p port 选项指定的端口上。如果没有找到 RMI 注册表,则在 jstatd 应用程序中创建一个,该应用程序绑定到由 -p port 选项指示的端口,或者在省略 -p port 选项时绑定到默认的 RMI 注册表端口。通过指定 -nr 选项,可以停止创建内部 RMI 注册表。
选项:
- -nr
当没有找到一个现有的 RMI 注册表时,不要尝试在 jstatd 进程中创建一个内部的 RMI 注册表。 - -p port
期望找到 RMI 注册表的端口号,如果没有指定 -nr 选项,则创建该端口号。 - -n rminame
远程 RMI 对象在 RMI 注册表中绑定到的名称。默认名称为 JStatRemoteHost。如果在同一主机上启动多个 jstatd 服务器,那么可以通过指定这个选项使每个服务器导出的 RMI 对象的名称惟一。但是,这样做需要在监视客户机的 hostid 和 vmid 字符串中包含唯一的服务器名称。 - -Joption
将 option 传递给 JVM,其中 option 是 Java 应用程序启动器参考页中描述的选项之一。例如,-J-Xms48m 设置启动内存为 48mb,请参见 java(1)。
剩下的看官方文档 可参考《IDEA 远程调试》,现在先不研究了,