JVM 调优 (2) -- 调优工具


关于常见的线上问题,我们多数会遇到以下问题:

  • 内存泄露
  • 某个进程突然cpu飙升
  • 线程死锁
  • 响应变慢…等等其他问题。

如果遇到了以上这种问题,在线下可以有各种本地工具支持查看,但到线上了,就没有这么多的本地调试工具支持,我们该如何基于监控工具来进行定位问题?

我们一般会基于数据收集来定位,而数据的收集离不开监控工具的处理,比如:运行日志、异常堆栈、GC日志、线程快照、堆快照等。经常使用恰当的分析和监控工具可以加快我们的分析数据、定位解决问题的速度。

1. JDK 命令行工具

JDK 的命令行工具大多数是对 jdk/lib/tools.jar 类库的一层薄包装而已,它们的主要功能代码是在 tools 类库中实现的。Linux 下的这些工具有的甚至是用 shell 脚本编写的。

SUN JDK 监控和故障处理工具:
JVM 调优 (2) -- 调优工具

1. jps:虚拟机进程状况工具

列出正在运行的虚拟机进程,并显示虚拟机执行的主类名称以及这些进程的本地虚拟机唯一 ID。

jps 命令格式: jps [options] [hostid]

  1. jps 可以通过 RMI 协议开启 RMI 服务的远程虚拟机进程状态,hostid 为 RMI 注册表中注册的主机名。
  2. 如果不指定 hostid 就默认为当前主机或服务器

options 选值:(options 的意思是可以同时使用多个参数选项)

  1. -q: 只输出LVMID,不输出类名、Jar名和传入main方法的参数
  2. -l: 输出 main 类或 Jar 的全限名
  3. -m: 输出传入 main 方法的参数
  4. -v: 输出传入 JVM 的参数

案例:
JVM 调优 (2) -- 调优工具
JVM 调优 (2) -- 调优工具

2. jstat:虚拟机统计信息监视工具

jstat 是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载 、内存 、垃圾回收 、JIT编译等运行数据,在没有 GUI 图形界面 、只提供了纯文本控制台环境的服务器上,它会是运行期定位虚拟机性能问题的首选工具。

jstat 的命令格式:jstat option vmid [interval [s|ms] [count]]

option 选值:

  1. -class:用于查看类加载情况的统计,如:jstat -class pid:显示加载 class 的数量,及所占空间等信息。
  2. -compiler:查看 HotSpot 中即时编译器编译情况的统计,如:jstat -compiler pid:显示 VM 实时编译的数量等信息。
  3. -printcompilation:查看 HotSpot 编译方法的统计,如:jstat -printcompilation pid:当前 VM 执行的信息。
  4. -gc:查看 JVM 中堆的垃圾收集情况的统计,如:jstat -gc pid:可以显示 gc 的信息,查看 gc 的次数及时间。其中最后五项,分别是 young gc 的次数,young gc 的时间,full gc 的次数,full gc 的时间,gc 的总时间。
  5. -gccapacity:查看新生代、老生代及持久代的存储容量情况,如:jstat -gccapacity pid:可以显示 VM 内存中三代(young,old,perm)对象的使用和占用大小。
  6. -gccause:查看垃圾收集的统计情况 (这个和 -gcutil 选项一样),如果有发生垃圾收集,它还会显示最后一次及当前正在发生垃圾收集的原因,如:jstat -gccause pid:显示 gc 原因。
  7. -gcnew:查看新生代垃圾收集的情况,如:jstat -gcnew pid:new 对象的信息。
  8. -gcnewcapacity:用于查看新生代的存储容量情况,如:jstat -gcnewcapacity pid:new 对象的信息及其占用量。
  9. -gcutil:查看新生代、老生代及持久代垃圾收集的情况,如:jstat -gcutil pid:统计 gc 信息。
  10. -gcold:用于查看老年代及持久代发生 GC 的情况,如:jstat -gcold pid:查看 old 对象的信息。
  11. -gcoldcapacity: 用于查看老生代的容量,如:jstat -gcoldcapacity pid:查看 old 对象的信息及其占用量。
  12. -gcpermcapacity:用于查看持久代的容量,如:jstat -gcpermcapacity pid:perm 对象的信息及其占用量。

案例:查看 gc 情况:jstat-gcutil 16856
JVM 调优 (2) -- 调优工具

3. jinfo:虚拟机配置信息查看工具

查看 VM 的配置信息。

命令格式:jinfo [option] pid

option 常用的选值:

  1. -h:查看可以使用的参数选项。
  2. -flag :查看 JVM 参数,如:jinfo -flag MaxMetaspaceSize pid:结果为 -XX:MaxMetaspaceSize=18446744073709486080
  3. -sysprops:查看 Java 的系统配置参数,如:jinfo -sysprops pid

案例:jinfo -flag MaxMetaspaceSize 16856
JVM 调优 (2) -- 调优工具

4. jmap:java 内存映像工具

jmap 命令用于生成堆转储快照。打印出某个 Java 进程(使用 pid )内存内的所有‘对象’的情况(如:产生哪些对象,及其数量)

jmap 的作用并不仅仅为了获取 dump 文件,它还可以查询 finalize 执行队列 、java 堆和永久代的详细信息,如空间使用率 、当前用的是哪种收集器等。

jmap 命令格式:jmap [option] vmid

option 常用的选值:

  1. -h:查看可以使用的参数选项。
  2. -dump:<path>:生成堆转储快照,如:jmap -dump:format=b,file=E:\hello.hprof 16856
    生成的文件用 VisualVM 装入可以得到:
    JVM 调优 (2) -- 调优工具
  3. -finalizerinfo:打印正等候回收的对象的信息。
  4. -heap:打印 Java 堆信息。
    JVM 调优 (2) -- 调优工具
  5. -histo:打印每个类的实例数目,内存占用,类全名信息。VM 的内部类名字开头会加上前缀 ”*”。如果 live 子参数加上后,只统计活的对象数量。
    JVM 调优 (2) -- 调优工具
    JVM 调优 (2) -- 调优工具
  6. -F:强迫在 pid 没有响应的时候使用 -dump 或者 -histo 参数,在这个模式下 live 子参数无效
    JVM 调优 (2) -- 调优工具
    JVM 调优 (2) -- 调优工具
  7. -permstat:打印 classload 和 jvm heap 持久层的信息。包含每个 classloader 的名字,活泼性,地址,父 classloader 和加载的 class 数量。另外,内部 String 的数量和占用内存数也会打印出来。
  8. -J<flag>:传递参数给 jmap 启动的 jvm。

5. jhat:虚拟机堆转储快照分析工具

JVM 快照分析工具,jhat 命令与 jamp 一般都是搭配使用的,用来分析 jmap 生成的堆快存储快照。jhat 内置了一个微型 http/Html 服务器,可以在浏览器上分析查看。不过建议尽量不要用该内置服务器,因为既然有 dump 文件,那么可以将文件从生产环境拉取下来,然后通过本地可视化工具来分析,这样既减轻了线上服务器压力,又能够分析得足够详尽 (比如 MAT/jprofile/visualVm ) 等。

参数选项:

  1. -h:查看可以使用的参数选项
  2. jhat <file>:分析生产的 dump 文件

案例:jhat E:\test.hprof

test.hprof 是 jmap 生成的 dump 文件,执行 jhat 命令后,当屏幕显示 Server is ready. 的提示后,用户在浏览器中键入 http://localhost:7000 就可以看到分析的结果了。

分析结果默认是以包为单位进行分组显示,分析内存泄漏问题主要会使用到其中的 “Heap Histogram” 与 OQL 标签的功能。前者可以找到内存中总容量最大的对象。后者是标准的对象查询语言,使用类似 SQL 的语法对内存中的对象进行查询统计。

登录 http://localhost:7000 后的界面:
JVM 调优 (2) -- 调优工具

6. jstack:java 堆栈跟踪工具

jstack 命令用于生成虚拟机当前时刻的线程快照(一般称为 threaddump 或者 javacore 文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程死锁 、死循环 、请求外部资源导致长时间等待等。

注意:尽量少用甚至不要用这个工具

jstack 格式:jstack [option] vmid

参数选项:

  1. -h:查看可以使用的参数选项
  2. -l:长列表,打印关于锁的附加信息,例如属于 java.util.concurrent 的 ownable synchronizers 列表
  3. -m:打印 java 和 native c/c++ 框架的所有栈信息
  4. -F:当jstack [-l] pid没有响应的时候强制打印栈信息

7. HSDIS:JIT 生成代码反汇编

在 Java 虚拟机规范中,详细描述了虚拟机指令集中每条指令的执行过程 、执行前后对操作数栈 、局部变量表的影响等细节。这些细节描述与 Sun 的早期虚拟机(Sun Classic VM)高度吻合,但随着技术的发展,高性能虚拟机真正的细节实现方式已经渐渐与虚拟机规范所描述的内容产生了越来越大的差距,虚拟机规范中的描述逐渐成了虚拟机实现的 “概念模型” ——即实现只能保证规范描述等效。基于这个原因,我们分析程序的执行语义问题(虚拟机做了什么)时,在字节码层面上分析完全可行,但分析程序的执行行为问题(虚拟机是怎样做的、性能如何)时,在字节码层面上分析就没有什么意义了,需要通过其他方式解决。

分析程序如何执行,通过软件调试工具(GDB、Windbg等)来断点调试是最常见的手段,但是这样的调试方式在 Java 虚拟机中会遇到很大困难,因为大量执行代码是通过 JIT 编译器动态生成到 CodeBuffer 中的,没有很简单的手段来处理这种混合模式的调试(不过相信虚拟机开发团队内部肯定是有内部工具的)。因此,不得不通过一些特别的手段来解决问题,基于这种背景,本节的主角—— HSDIS 插件就正式登场了。

HSDIS 是一个 Sun 官方推荐的 HotSpot 虚拟机 JIT 编译代码的反汇编插件,它包含在 HotSpot 虚拟机的源码之中,但没有提供编译后的程序。在 Project Kenai 的网站也可以下载到单独的源码。它的作用是让 HotSpot 的 -XX:+PrintAssembly 指令调用它来把动态生成的本地代码还原为汇编代码输出,同时还生成了大量非常有价值的注释,这样我们就可以通过输出的代码来分析问题。读者可以根据自己的操作系统和 CPU 类型从 Project Kenai 的网站上下载编译好的插件,直接放到 JDK_HOME/jre/bin/clientJDK_HOME/jre/bin/server 目录中即可。如果没有找到所需操作系统(譬如 Windows 的就没有)的成品,那就得自己使用源码编译一下。

还需要注意的是,如果读者使用的是 Debug 或者 FastDebug 版的 HotSpot,那可以直接通过 -XX:+PrintAssembly 指令使用插件;如果使用的是 Product 版的 HotSpot,那还要额外加入一个 -XX:+UnlockDiagnosticVMOptions 参数。

2. JDK 可视化工具

对 JVM 监控的常见可视化工具,除了 jdk 本身提供的 JConsole 和 VisualVm 之外,还有第三方提供的 jprofilter 、perfino 、Yourkit 、Perf4j 、JProbe 、MAT 等。这些工具都极大的丰富了我们定位以及优化 JVM 的方式。

这些工具的使用,网上有很多教程提供,这里就不再过多介绍了。对于 VisualVm 来说,比较推荐使用,它除了对 JVM 的侵入性比较小之外,还是 jdk 团队自己开发的,相信以后功能会更加丰富和完善。对于第三方监控工具 jprofilter,它提供的功能和可视化最为完善,目前多数 IDE 都支持该插件,对于上线前的调试以及性能调优可以配合使用。

另外对于线上 dump 的 heap 信息,应该尽量拉到线下,然后用可视化工具来分析,这样分析更加详细。如果对于一些紧急的问题,必须需要通过线上监控,可以采用 VisualVm 的远程功能来进行,这需要使用 tool.jar 下的 MAT 功能。

1. JConsole

JConsole 工具在 JDK/bin 目录下,启动 JConsole 后,将自动搜索本机运行的 JVM 进程,不需要 jps 命令来查询指定。双击其中一个 JVM 进程即可开始监控,也可使用 远程进程 来连接远程服务器。

2. VisualVM:多合一故障处理工具

VisualVM 是一个集成多个 JDK 命令行工具的可视化工具。VisualVM 基于 NetBeans 平台开发,它具备了插件扩展功能的特性,通过插件的扩展,可用于显示虚拟机进程及进程的配置和环境信息 (jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息 (jstat、jstack)等。VisualVM 在 JDK/bin 目录下。

VisualVM 的性能分析功能甚至比起 JProfiler、YourKit 等专业且收费的 Profiling 工具都不会逊色多少,而且 VisualVM 还有一个很大的优点:不需要被监视的程序基于特殊 Agent 运行,因此它对应用程序的实际性能的影响很小,使得它可以直接应用在生产环境中。这个优点是 JProfiler、YourKit 等工具无法与之媲美的。