4.1 概述
经过前面两章对于虚拟机的内存分配与回收技术各方面的介绍,借下来,我们来从实践的角度去了解虚拟机内存管理的世界。
给定一个系统定位问题的时候,知识、经验是关键基础, 数据是依据,工具是运用知识处理数据的手段,这里说的,数据包括:运行日志、 异常堆栈、GC 日志、线程快照(threaddump/ javacore文件)、堆转储快照(headdump/ hprof 文件)等。经常使用适当的虚拟机监控和分析的工具永远是知识技能的一层包装,没有什么工具是 “秘密武器” ,不可能学会了就能包治百病。
4.2 JDK的命令行工具
Java开发人员可能知道 JDK 的 bin 目录中有 “java.exe” 、“javac.exe” 这两个命令行工具,但并非所有程序员都了解过JDK的bin目录之中其他命令行程序的作用。 每逢 JDK 更新版本之时, bin 目录下命令行工具的数量和功能会不知不觉地增加和增强。 bin 目录的内容如下所示:
在本章中,将介绍这些工具中的其中一部分, 主要包括**用于监视虚拟机和故障处理的工具。**这些故障处理工具被 Sun 公司作为“礼物” 附赠给 JDK 的使用者, 并在软件的使用说明中把它们声明为 “没有技术支持并且是实验性质的 ” (unsupported and experimental) 的产品,但事实上,这些工具都非常稳定并且功能强大, 能在处理应用程序性能问题、定位故障时发挥很大的作用。
说起JDK的工具,大家可能会发现这些工具的程序体积都异常小巧,集合所有工具的体积基本上都稳定在 27 KB左右。并非 JDK开发团队刻意把它们制作的如此精炼来炫耀编程水平,而是因为这些命令行工具大多数是 jdk/ lib/ tools.jar 类库的一层薄包装而已,它们主要的功能代码是在 tools 类中实现的。
JDK开发团队选择采用 Java 代码来实现这些监控工具是有特别用意的; 当应用程序部署到生产环境后, 无论是直接接触物理服务器还是远程 Telnet 到服务器上都可能会受到限制、 借助 tools.jar 类库里面的接口,我们可以直接在应用程序中实现功能强大的监控分析功能。
4.2.1 jps: 虚拟机进程状况工具
JDK的很多小工具的名字都参考了UNIX 命令的命名格式, jps(JVM Process Status Tool)是其中的典型, 除了名字像UNIX的 ps 命令外,它的功能也和 ps 命令类似 :
可以列出正在进行的虚拟机进程,并显示虚拟机执行主类(Main Class, main() 函数所在的类)名称以及这些进程的本地虚拟机唯一 ID( Local Virtual Machine Identifier, LVMID)。
虽然功能比较单一,但它是使用频率最高的JDK命令行工具,因为其他的 JDK 工具大多需要输入它查询到的 LVMID 来确定要监控的是哪一个虚拟机进程。
对于本地虚拟机进程来说, LVMID 与操作系统的进程 ID(Process Identifier, PID) 是一致的, 使用 Windows 的任务管理器或者 UNIX 的 ps 命令也可以查询到虚拟机进程的 LVMID ,但如果同时启动了多个虚拟机进程,无法根据进程名称定位时,那就只能依靠 jps 命令显示主类的功能才能区分了。
jps 命令格式:
jps [ options ] [ hostid ]
jps 可以通过 RMI 协议(?)查询开启了 RMI 服务的远程虚拟机进程状态,hostid 为 RMI 注册表中注册的主机名, jps 的其他常用选项表:
4.2.2:虚拟机统计信息监视工具
jstat(JVM Statistics- Monitoring Tool) 是用于监视虚拟机各种运行状态信息的命令行工具。 **它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、 JIT编译等运行数据,**在没有 GUI 图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期间定位虚拟机性能的首选工具。
jstat 命令格式为:
jstat [option vmid [interval [s| ms] [count ] ] ]
对于命令格式中的 VMID 与 LVMID 需要特别说明一下: 如果是本地虚拟机进程, VMID 与 LVMID 是一致的, 如果是远程虚拟机进程, 那 VMID的格式应当是:
[protocol:] [ // lvmid [@hostname [:port] / servername]
参数 interval 和 count 代表查询间隔和次数, 如果省略这两个参数,说明只查询一次。 假设需要每 250毫秒查询一次进程 2764 垃圾收集情况,一共查询 20 次, 那命令应当是:
jstat -gc 2764 250 20
选项 option 代表着用户希望查询的虚拟机信息, 主要分为 3 类: 类装载、 垃圾收集、 运行期编译状况, 具体选项及作用参考如下;
jstat 监视选项众多。
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
PC:Perm(持久代)的容量 (字节)
PU:Perm(持久代)目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
NGCMN:年轻代(young)中初始化(最小)的大小 (字节)
NGCMX:年轻代(young)的最大容量 (字节)
NGC:年轻代(young)中当前的容量 (字节)
OGCMN:old代中初始化(最小)的大小 (字节)
OGCMX:old代的最大容量 (字节)
OGC:old代当前新生成的容量 (字节)
PGCMN:perm代中初始化(最小)的大小 (字节)
PGCMX:perm代的最大容量 (字节)
PGC:perm代当前新生成的容量 (字节)
S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
O:old代已使用的占当前容量百分比
P:perm代已使用的占当前容量百分比
S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节)
S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节)
ECMX:年轻代中Eden(伊甸园)的最大容量 (字节)
DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满)
TT:持有次数限制
MTT :最大持有次数限制
使用 jstat 工具在纯文本状态下监视虚拟机状态的变化,确实不如后面将会提到的 VIsualVM 等可视化的监视工具直接以图表展现那样直观,但许多服务器管理员都习惯了在文本控制台工作, 直接在控制台中使用 jstat 命令依然是一种常用的监控方式。
4.2.3 jinfo:Java配置信息工具
jinfo( Configuration Info for Javva) 的作用是实时地查看和调查虚拟机各项参数, 使用 jps 命令的 -v 参数可以查看虚拟机启动时显式指定的参数列表, 但如果想知道未被显式指定的参数的系统默认值, 除了去找资料外, 就只能使用 jinfo 的 -flag 选项进行查询了
jinfo 还可以使用 -sysprops 选项把虚拟机进程的 System.getProperties() 的内容打印出来。
jinfo 命令格式:
jinfo {option} pid
4.2.4 jmap: Java内存映像工具
jmap(Memory Map for Java) 命令用于生成堆转储快照(一般称为 heapdump 或 dump 文件)。如果不使用 jmap 命令,要想获取 Java 堆转储快照, 还有一些比较 “暴力” 的手段, 譬如之前用过的 -XX: +HeadDumpOnOutOfMemoryError 参数, 可以让虚拟机在 OOM 异常出现之后自动生成 dump 文件。
jmap 的作用不仅仅是为了获取 dump 文件, 它还可以查询 finalize 执行队列、 Java堆和永久代的详细信息, 如空间使用率、 当前用的是那种收集器等。
和 jinfo 命令一样, jmap 有不少功能在 windows 平台上都是受限的, 除了生成 dump 文件的 -dump 选项用于查看每一个类的实例, 空间占用统计的 -histo 选项在所有操作系统都提供之外, 其余选项都只能在 Linux / Solaris 下使用
jmap 命令格式:
jmap [ option ] vmid
option 选项的合法值与具体含义:
4.2.5 jhat: 虚拟机堆转储快照分析工具
Sun JDK 提供 jhat(JVM Heap Analysis Tool) 命令与 jmap 搭配使用, 来分析 jmap 生成的堆转储快照。
jhat 内置了一个微型的 HTTP/HTML 服务器, 生成 dump文件(这到底是个啥)的分析结果后, 可以在浏览器中查看, 不过实事求是,除非真的没有别的工具可用,否则也不会用 jhat 命令来分析 dump 文件, 主要原因有二:
- 一是一般不会在部署应用程序的服务器上直接分析 dump 文件,即使这样做,也会尽量将 dump 文件复制到其他机器上进行分析, 因为分析工作是一个耗时而且消耗硬件资源的过程,既然都要在其他机器进行, 就没有必要受到命令行工具的限制了。
- 另一个原因是 jhat 的分析功能相对捡漏, 后文的 VisualVM ,以及专业用于分析 dump 文件的 Eclipse Memory Analyzer、IBM HeapAnalyzer 等工具, 都能实现比 jhat 更强大更专业的分析功能。
扩展来源:https://www.zhihu.com/question/285731828
在计算机领域,dump一般译作转储。即使翻译的很贴切,但还是很难从字面上完全理解dump的真正含义。
dump有动词和名词两种场景,我先解释它作为动词的情况,名词自然就理解了。作为动词,我推荐你可以从dump的目的和dump的对象,这两个方面去理解dump本身。
1、为什么要dump(dump的目的)?因为程序在计算机中运行时,在内存、CPU、I/O等设备上的数据都是动态的(或者说是易失的),也就是说数据使用完或者发生异常就会丢掉。如果我想得到某些时刻的数据(有可能是调试程序Bug或者收集某些信息),就要把他转储(dump)为静态(如文件)的形式。否则,这些数据你永远都拿不到。
2、dump转储的是什么内容(dump的对象)?其实上边已经提到了,就是将动态(易失)的数据,保存为静态的数据(持久数据)。像程序这种本来就保存在存储介质(如硬盘)中的数据,也就没有必要dump。现在,dump作为名词也很好理解了,一般就是指dump(动词)的结果文件。
常出现dump的场景:Unix/Linux中的coredump,Java中的headdump和threaddump,还有就是你说的tcpdump工具。
4.2.6 jstack : Javva 堆栈跟踪工具
jstack( Stack Trace for Java ) 命令用于生成虚拟机当前时刻的线程快照(一般称为 threaddump 或者 javacore 文件)。
线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的常见原因,如线程间死锁、死栈的集合、请求外部资源导致的长时间等待等等都是导致线程长时间停顿的常见原因,线程出现停顿的时候通过 jstack 来查看各个线程的调用堆栈, 就可以知道没有相应的线程到底在后台做些什么事情,或是等待着什么资源。
JavaCore/HeapDump文件拓展来源:http://blog.sina.com.cn/s/blog_55eccf2101013od0.html
文件产生的时间:
Java程序运行时,有时会产生JavaCore及HeapDump文件,它一般发生于Java程序遇到致命问题的情况下。
有时致命问题发生后,Java应用不会死掉,还能继续运行;
但有时致命问题发生,Java进程会死掉;
为了能够保留Java应用发生致命错误前的运行状态,JVM在死掉前产生两个文件,分别为JavaCore及HeapDump文件。
有何区别:
JavaCore是关于CPU的,而HeapDump文件是关于内存的。
JavaCore文件主要保存的是Java应用各线程在某一时刻的运行的位置,即JVM执行到哪一个类、哪一个方法、哪一个行上。它是一个文本文件,打开后可以看到每一个线程的执行栈,以stack trace的显示。通过对JavaCore文件的分析可以得到应用是否“卡”在某一点上,即在某一点运行的时间太长,例如数据库查询,长期得不到响应,最终导致系统崩溃等情况。
HeapDump文件是一个二进制文件,它保存了某一时刻JVM堆中对象使用情况,这种文件需要相应的工具进行分析,如IBM Heap Analyzer这类工具。这类文件最重要的作用就是分析系统中是否存在内存溢出的情况。
文件时如何生成:
这两个文件可以用手工的方式生成,当我们会遇到系统变慢或无响应的情况,这时就以采用手工的方式生成JavaCore及HeapDump文件。
如何分析
JavaCore文件
两组文件在分析JavaCore时特别有效,因为它可以看出在先后两个时间点上,线程执行的位置,如果发现先后两组数据中同一线程都执行在同一位置,则说明此处可能有问题,因为程序运行是极快的,如果两次均在某一点上,说明这一点耗时是很大的,通过对这两个文件进行分析,查出原因,进而解决问题。
JavaCore文件的头部有一个“Current Thread Details”标记,它记录了JavaCore产生时系统运行的线程id,使用线程id在文件中查找线程的详细信息,该信息中记载了线程运行哪个类的时候造成的HeapDump文件
HeapDump文件是指定时刻的Java堆栈的快照,是一种镜像文件。Heap Analyzer工具通过分析HeapDump文件,哪些对象占用了太多的堆栈空间,来发现导致内存泄露或者可能引起内存泄露的对象。
HSDIS : JIT生成代码反汇编
在Java虚拟机规范中,详细描述了虚拟机指令集中每条指令的执行过程、执行前后对操作数栈、局部变量表的影响等细节。
这些细节描述与Sun的早期虚拟机高度吻合, 但随着技术的发展,高性能虚拟机真正的细节实现方式已经渐渐与虚拟机规范所描述的内容产生了越来越大的差距, 虚拟机规范中的描述逐渐成了虚拟机实现的“概念模型” ——即实现只能保证规范描述等效。
基于这个原因,我们分析程序的执行语义问题(虚拟机做了什么)时, 在字节码层面上分析完全可行,但分析程序的执行行为问题(虚拟机是怎样做的、性能如何)时,字节码层面上分析就没有什么意义了, 需要通过其它方式解决。
分析程序如何执行, 通过软件调试工具(GDB、Windbg等)来断点调试是常见的手段,但是这种调试方式在Java虚拟机中会遇到很大困难。
HSDIS 是一个 Sun 官方推荐的 HotSpot 虚拟机 JIT 编译代码的反汇编插件, 它包含在 HotSpot 虚拟机的源码之中, 但没有提供编译后的程序。在 Project Kenai 的网站也可以下载到单独的源码。
它的作用是让 HotSpot 的 -XX: +PrintAssembly 指令调用它来把动态生成的本地代码还原成汇编代码输出, 同时还生成了大量非常有价值的注释,这样我们就可以通过输出的代码来分析问题。
4.3 JDK的可视化工具
JDK中除了大量的命令行工具外,还有两个功能强大的可视化工具: JConsol 和 VisualVM, 这两个工具是 JDK 的正式成员,没有被贴上 “ unsupported and experimental” 的标签。
4.3.1 JConsole: Java监视与管理控制台
JConsole(Java Monitoring and Management Console) 是一种基于 JMX的可视化监视、管理工具, 它管理的部分的功能是针对 JMX MBean 进行管理, 由于 MBean 可以使用代码、中间件服务器的管理控制台或者所有符合 JMX 规范的软件进行访问, 所以本节将会着重介绍 JConsole 监视部分的功能。
- 启动 JConsole
- 主界面
选择进程双击进入 - 概览窗口