定位问题,知识储备是基础,日志等数据是依据,工具则是帮助我们事半功倍的手段。
本文是在win下测试,主要介绍一些工具的使用。
1.jps:虚拟机进程状况工具
JVM Process Status Tool
可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier)。
C:Users***Desktop64124-深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)_源码>jps -l 39172 sun.tools.jps.Jps 24396 24476 org.jetbrains.jps.cmdline.Launcher
2.jstat:虚拟机统计信息工具
JVM Statistics Monitoring Tool
jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程[插图]虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据,在没有GUI图形界面、只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的常用工具。
C:Users***Desktop64124-深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)_源码>jstat -gcutil 24476 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 75.50 0.00 76.86 0.20 97.04 92.93 2 0.009 0 0.000 0.009
查询结果表明:这台服务器的新生代Eden区(E,表示Eden)使用了76.8%的空间,2个Survivor区(S0、S1,表示Survivor0、Survivor1),老年代(O,表示Old)和永久代(P,表示Permanent)则分别使用了0.2%和97.4%的空间(这里没有P只有M,对照看的话应该就是P)。程序运行以来共发生MinorGC(YGC,表示Young GC)2次,总耗时0.1009秒;发生Full GC(FGC,表示Full GC)0次,总耗时(FGCT,表示Full GC Time)为0秒;所有GC总耗时(GCT,表示GC Time)为0.009秒。
3.jinfo:Java配置信息
configuration info for java
这里也就是查-XX:....这类的配置
C:Users***Desktop64124-深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)_源码>jinfo -flag CMSInitiatingOccupancyFraction 24476 -XX:CMSInitiatingOccupancyFraction=-1
4.jmap:java内存映像工具
memory map for java
jmap(Memory Map for Java)命令用于生成堆转储快照(一般称为heapdump或dump文件)。如果不使用jmap命令,要想获取Java堆转储快照也还有一些比较“暴力”的手段:譬如在第2章中用过的-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在内存溢出异常出现之后自动生成堆转储快照文件,通过-XX:+HeapDumpOnCtrlBreak参数则可以使用[Ctrl]+[Break]键让虚拟机生成堆转储快照文件,又或者在Linux系统下通过Kill-3命令发送进程退出信号“恐吓”一下虚拟机,也能顺利拿到堆转储快照。对于IDEA的话还有快捷键。
C:Users***Desktop64124-深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)_源码>jmap -dump:format=b,file=eclipse.bin 24476 Dumping heap to C:UsershufanDesktop64124-深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)_源码eclipse.bin ... Heap dump file created
5.jhat:堆转储快照分析
jvm heap analysis tool
主要是分析jmap的堆快照,基本没人用
C:Users***Desktop64124-深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)_源码>jhat eclipse.bin Reading from eclipse.bin... Dump file created Mon Feb 03 13:25:03 CST 2020 Snapshot read, resolving... Resolving 3026440 objects... Chasing references, expect 605 dots............................................................................................................................ ............................................................................................................................................................... ............................................................................................................................................................... ............................................................................................................................................................... .... Eliminating duplicate references............................................................................................................................... ............................................................................................................................................................... ............................................................................................................................................................... ............................................................................................................................................................... . Snapshot resolved. Started HTTP server on port 7000 Server is ready.
这时打开浏览器输入 http://localhost:7000/ 可以看分析结果了。
6.jsatck:java堆栈跟踪工具
stack trace for java
这个主要是查看各个线程堆栈
C:Users***Desktop64124-深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)_源码>jstack 24396 2020-02-03 13:29:05 Full thread dump OpenJDK 64-Bit Server VM (25.202-b44 mixed mode): "JobScheduler FJ pool 4/7" #170 daemon prio=4 os_prio=-1 tid=0x00000000242dd000 nid=0x2d6c waiting on condition [0x000000004662f000] java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000000e33ce9b0> (a java.util.concurrent.ForkJoinPool) at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) "JobScheduler FJ pool 3/7" #169 daemon prio=4 os_prio=-1 tid=0x00000000242dc800 nid=0x9770 waiting on condition [0x000000004652f000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000000e33ce9b0> (a java.util.concurrent.ForkJoinPool) at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
从JDK 5起,java.lang.Thread类新增了一个getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。使用这个方法可以通过简单的几行代码完成jstack的大部分功能。
java的一些命令行工具,可以自行google,这种东西没必要记,只需要在大脑里有印象即可。
介绍两个可视化工具
jhsdb,这个工具貌似只有openjdk才有。
案例分析。
/** * staticObj、instanceObj、localObj存放在哪里? */ public class JHSDBTestCase { static class Test { static ObjectHolder staticObj = new ObjectHolder(); ObjectHolder instanceObj = new ObjectHolder(); void foo() { ObjectHolder localObj = new ObjectHolder(); System.out.println("done"); // 这里设一个断点 } } private static class ObjectHolder {} public static void main(String[] args) { Test test = new JHSDBTestCase.Test(); test.foo(); } }
这个不用工具分析也可以知道,staticObj随着Test的类型信息存放在方法区,instanceObj随着Test的对象实例存放在Java堆,localObject则是存放在foo()方法栈帧的局部变量表中。
这里我们用工具分析
1.命令行进入图形化界面
jhsdb hsdb --pid 11180(你当前运行的线程)
2.点击菜单中的Tools->Heap Parameters[插图],结果如图4-5所示,因为笔者的运行参数中指定了使用的是Serial收集器,图中我们看到了典型的Serial的分代内存布局,Heap Parameters窗口中清楚列出了新生代的Eden、S1、S2和老年代的容量(单位为字节)以及它们的虚拟内存地址起止范围。
3.使用scanoops命令在Java堆的新生代(从Eden起始地址到To Survivor结束地址)范围内查找ObjectHolder的实例,结果如下所示:
可见这三个对象全部落在了新生代Eden,我们继续分析,再使用revptrs
这个命令大致的作用就是通过反推实例地址,然后我们再用Tool里的inspector工具
JDK 7及其以后版本的HotSpot虚拟机选择把静态变量与类型在Java语言一端的映射Class对象存放在一起,存储于Java堆之中,从我们的实验中也明确验证了这一点[插图]。
然后第二个对象
这次找到一个类型为JHSDB_TestCase$Test的对象实例,在Inspector中该对象实例显示如图4-8所示。
第三个方法栈却不行了。
看来revptrs命令并不支持查找栈上的指针引用,不过没有关系,得益于我们测试代码足够简洁,人工也可以来完成这件事情。在Java Thread窗口选中main线程后点击Stack Memory按钮查看该线程的栈内存,如图4-9所示。
肉眼看!
参考周志明老师《深入理解Java虚拟机》