“不尚贤,使民不争;不贵难得之货,使民不为盗;不见可欲,使民心不乱。
是以圣人之治,虚其心,实其腹,弱其志,强其骨,常使民无知无欲。
使夫知者不敢为也。
为无为,则无不治。”^ddj
JVM为我们提供了很多工具,我们可以用它来监测运行中的程序,对于程序优化和问题分析很有帮助,今天就聊聊这方面的操作。
大致涉及如下内容:
jps
jps命令可以显示当前运行java进程以及相关参数。
1 | jps -q //只显示pid,不显示class名称,jar文件名和传递给main 方法的参数 |
1 | jps -m //输出传递给main 方法的参数 |
1 | jps -l //输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名 |
jstack
jstack是虚拟机堆栈追踪工具,用于生成虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
线程状态:
1 | NEW 未启动的 |
2 | RUNNABLE 在虚拟中运行的 |
3 | BLOCKED 受阻塞并等待监视器锁 |
4 | WAITTING 无限期等待另一个线程执行特定操作 |
5 | TIMED_WAITTING 有限期的等待另一线程执行特定操作 |
6 | TERMINATED 已退出的 |
1 | jstack [options] <pid> //pid可以通过jps或者top获取,后面给出的具体分析实例会写到 |
1 | jstack -l 长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表. |
1 | jstack -m 打印java和native c/c++框架的所有栈信息. |
jstat
jstat(JVM Statistics Monitoring Tool)是用于监控虚拟机各种运行状态信息的命令行工具。他可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
1 | jstat -class <pid> 显示加载class的数量,及所占空间等信息。 |
2 |
|
3 | 结果说明: |
4 | Loaded 装载的类的数量 |
5 | Bytes 装载类所占用的字节数 |
6 | Unloaded 卸载类的数量 |
7 | Bytes 卸载类的字节数 |
8 | Time 装载和卸载类所花费的时间 |
1 | jstat -gc <pid>: 可以显示gc的信息,查看gc的次数,及时间。 |
2 |
|
3 | 结果说明: |
4 | S0C 年轻代中第一个survivor(幸存区)的容量 (字节) |
5 | S1C 年轻代中第二个survivor(幸存区)的容量 (字节) |
6 | S0U 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) |
7 | S1U 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) |
8 | EC 年轻代中Eden(伊甸园)的容量 (字节) |
9 | EU 年轻代中Eden(伊甸园)目前已使用空间 (字节) |
10 | OC Old代的容量 (字节) |
11 | OU Old代目前已使用空间 (字节) |
12 | PC Perm(持久代)的容量 (字节) |
13 | PU Perm(持久代)目前已使用空间 (字节) |
14 | YGC 从应用程序启动到采样时年轻代中gc次数 |
15 | YGCT 从应用程序启动到采样时年轻代中gc所用时间(s) |
16 | FGC 从应用程序启动到采样时old代(全gc)gc次数 |
17 | FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
18 | GCT 从应用程序启动到采样时gc用的总时间(s) |
jmap
jmap是JDK自带的工具软件,主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。
常见内存错误:
1 | OutOfMemoryError 年老代内存不足。 |
2 | OutOfMemoryError:PermGen Space 永久代内存不足。 |
3 | OutOfMemoryError:GC overhead limit exceed 垃圾回收时间占用系统运行时间的98%或以上。 |
options
1 | <no option> 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。 |
2 | -dump:[live,]format=b,file=<filename> 以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。 |
3 | -finalizerinfo 打印等待终结的对象信息。 |
4 | -heap 打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。 |
5 | -histo[:live] 打印堆的柱状图。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。 |
6 | -permstat 打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。 |
7 | -F 强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。 |
8 | -h 打印帮助信息。 |
9 | -help 打印帮助信息。 |
10 | -J<flag> 指定传递给运行jmap的JVM的参数。 |
实战
下面以一个简单的例子,分析出运行项目中最消耗cpu的线程并定位代码。
- 得到进程id
jps or ps,以ps为例:1 | ps -ef | grep Kernel | grep -v grep |
得到进程id 即pid
然后查找该进程id下各个线程中最消耗的那个线程tid,用top指令:
得到线程id 即tid,由于jstack中tid和nid都是十六进制的,因此需要将得到的tid转换为十六进制:
将得到的十六进制tid,用jstack即可定位具体线程:1 | jstack <pid> | grep <tid十六进制> |