JDK自带工具之问题排查场景示例

标签: jdk 工具 问题 | 发表时间:2016-11-16 14:21 | 作者:
出处:http://www.rowkey.me/

目录

最近看到了大量关于java性能调优、故障排查的文章,自己也写了一篇 Java调优经验谈。接着此篇文章,其实一直打算写写一些常用调优的工具以及惯常用法的。后来在 http://java-performance.info这个站点上看到了类似的一篇博文,自我感觉很有指导意义。于是决定翻译+重组织一下此篇文章: Java server application troubleshooting using JDK tools

引言

在Java世界中,我们的很多开发工作从编码、调试到调优都是在使用GUI工具进行。我们经常尝试在本地构建一套和生产环境一样的环境从而使得问题能够重现,进而使用我们常用的工具来排查定位故障。但不幸的是,很多情况下我们是无法在本地重现线上问题的。例如,我们是没有权限获取线上真实客户端提交到服务端的数据的。

由于上文提到的问题,很多时候都是需要远程来排查线上服务器上发生的问题。但是如果单单只有一个JRE的话,你也是无法有合适的方案来进行排查的。你需要JDK或者第三方的工具。有时候使用JDK提供的工具就是最可取的方案,毕竟,在线上环境使用第三方工具有时候会牵扯到权限的问题。

一般情况下,在线上环境安装JDK发布版本可以让排查进行地更高效。建议安装使用最新的Java7/8 JDK或者构建与线上JRE匹配的一些工具(原文作者不建议安装jdk的发布版本,而是建议根据需求逐渐地安装其中的工具)。

问题排查场景

获取正在运行的JVM列表

为了开始排查工作,我们首先需要获取正在运行的jvm进程列表,包括进程id、命令行参数等。有时候仅仅这一步就可以定位到问题,例如,同样的app被多启动一次在并发做同样的事情(破坏输出文件、重新打开sockets后者其他愚蠢的事情)。

使用 jcmd不加任何参数即可获取jvm进程列表

  25691 org.apache.catalina.startup.Bootstrap start
20730 org.apache.catalina.startup.Bootstrap start
26828 sun.tools.jcmd.JCmd
3883 org.apache.catalina.startup.Bootstrap start

使用 jcmd help能够获取某个jvm进程其他可用的诊断命令。例如:

  [root@test-172-16-0-34-ip ~]# jcmd 3883 help
3883:
The following commands are available:
VM.commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
Thread.print
GC.class_histogram
GC.heap_dump
GC.run_finalization
GC.run
VM.uptime
VM.flags
VM.system_properties
VM.command_line
VM.version
help

输入 jcmd 可以运行一个诊断命令或者获取到参数错误信息。

  [root@test-172-16-0-34-ip ~]# jcmd 3883 GC.heap_dump
3883:
java.lang.IllegalArgumentException: Missing argument for diagnostic command 

通过 jcmd help 你能够获取此诊断命令更多的信息。如下是 GC.heap_dump命令的help。

  [root@test-172-16-0-34-ip ~]# jcmd 3883 help GC.heap_dump
3883:
GC.heap_dump
Generate a HPROF format dump of the Java heap.

Impact: High: Depends on Java heap size and content. Request a full GC unless the '-all' option is specified.

Syntax : GC.heap_dump [options] <filename>

Arguments:
    filename :  Name of the dump file (STRING, no default value)

Options: (options must be specified using the <key> or <key>=<value> syntax)
    -all : [optional] Dump all objects, including unreachable objects (BOOLEAN, false)  

Java堆的DUMP

jcmd提供了输出HPROF格式的堆dump接口。运行 jmcd GC.heap_dump 即可。注意这里的FILENaME是相对于运行中的jvm目录在说的,因此推荐使用绝对路径。此外,也建议使用.hprof作为输出文件的扩展名。

在堆dump完成之后,你可以复制此文件到本地用VisualVM或者用jmc的JOverflow插件打开,进而通过分析堆的状况定位内存问题。

需要注意的两点:

  • 还有很多可以打开分析hprof文件的工具:NetBeans, Elipse的MAT,jhat等等。用你最熟悉的即可。
  • 同样可以使用 jmap -dump:live,file= 来产生堆dump文件,但是官方文档标注了此工具为unsupported的。虽然我们绝大多数人都会认为JDK中unsupported的特性会永远存在,但是事实并非这样: JEP 240, JEP 241

分析类柱状图

如果正在排查内存泄漏问题,你可能想要知道堆中某种类型的存活对象数目。例如,某一时刻某些类应该只有一个实例(单例模式),但是此类的另外一个或者多个实例却已经到了老年代,但是它们不应该能被GC roots访问到。

运行以下命令可以打印出类柱状图(同时也打印出存活对象的数目):

  jcmd <PID> GC.class_histogram
jmap -histo:live <PID>

输入如下:

      num     #instances         #bytes  class name
----------------------------------------------
   1:         37083       48318152  [B
   2:        235781       22496784  [C
   3:        103958       16069448  <constMethodKlass>
   4:        482361       15435552  java.util.HashMap$Entry
   5:        103958       14152480  <methodKlass>
   6:          9576       11192168  <constantPoolKlass>
   7:        186264       10430784  com.mysql.jdbc.ConnectionPropertiesImpl$BooleanConnectionProperty
   8:        274109        8771488  java.util.Hashtable$Entry
   9:          9576        7210152  <instanceKlassKlass>
  10:          7972        6404256  <constantPoolCacheKlass>
  11:        229637        5511288  java.lang.String
  12:         48471        5428752  java.net.SocksSocketImpl
  13:         21599        3859672  [Ljava.util.HashMap$Entry;

这里的以byte为单位的占用大小是浅的(shallow size),并没有包括子对象的大小。其实很容易注意到char[]和String的统计数据:这俩的实例数目是差不多的,但是char[]的占用大小要大很多,这在于String并未包含下面的char[]的大小。

有了类柱状图信息,你就可以grep/search类的名字从而获取存活实例的数目。如果你发现比期望的数目要大很多,你就可以做heap dump,然后用任意的heap分析工具来分析问题。

线程Dump

很多时候,应用会呈现出“卡在那里”的情形。这里有很多种卡住的状况:死锁、cpu密集运算。为了定位到问题所在需要知道线程在做什么、持有了什么锁等等。

Java中有两种锁:sychronized和Object.wait/notifyAll方法的原始锁以及java5引入的java.util.concurrent锁。这俩种锁的不同之处主要在于前者是限制在你进入synchronies块的地方的栈帧(stack frame)中,并且会一直在线程dump中存在。后者却并不限制在栈帧中-你可以在一个方法中进入锁,在另一方法中解锁。因此,thread dump有时候并没有包含这些信息。尽管如此,还是应该使用thread dump来查看线程信息排查问题。

这里有三种方法可以打印应用的thread dump。

  kill -3 <PID> #仅限Linux平台
jstack <PID>
jcmd <PID> Thread.print

运行Java飞行记录器(Java Flight Recorder)

上面讲到的工具都是作为快速的查看诊断工具的。如果要深入分析问题,可以选择使用内置的Java飞行记录器: Java Mission Control

运行JFR需要三步:

  1. 创建一个包含了你自己配置的JFR模板文件。运行 jmc, 然后 Window->Flight Recording Template Manage菜单。准备好档案后,就可以导出文件,并移动到要排查问题的环境中。

  2. 由于JFR需要JDK的商业证书,这一步需要解锁jdk的商业特性。

         jcmd <PID> VM.unlock_commercial_features
    
  3. 最后你就可以启动JFR。

         jcmd <PID> JFR.start name=test duration=60s settings=template.jfc filename=output.jfr
    

    上述命令立即启动JFR并开始使用 templayte.jfc的配置收集60s的JVM信息,输出到 output.jfr中。

一旦记录完成之后,就可以复制.jfr文件到你的工作环境使用jmc GUI来分析。它几乎包含了排查jvm问题需要的所有信息,包括堆dump时的异常信息。

后记

本文基本上是对英文原文的翻译,主要描述了几个常见问题的排查场景。

不得不说的是,JDK自带的工具是非常强大的。用好了这些工具其实已经足以应付绝大多数的Java问题排查场景。

相关 [jdk 工具 问题] 推荐:

JDK自带工具之问题排查场景示例

- - 后端技术杂谈 | 飒然Hang
获取正在运行的JVM列表. 运行Java飞行记录器(Java Flight Recorder)). 最近看到了大量关于java性能调优、故障排查的文章,自己也写了一篇 Java调优经验谈. 接着此篇文章,其实一直打算写写一些常用调优的工具以及惯常用法的. 后来在 http://java-performance.info这个站点上看到了类似的一篇博文,自我感觉很有指导意义.

JDK自带监控工具

- - ITeye博客
         系统在生产运行过程中最重要的工作莫过于监控与问题的处理,监控是预防问题产生很重要的手段. 在监控过程中可以发现那些模块或进程出现了问题,出现问题后会及时通知问题负责人.         实现监控的手段非常多,有系统级别监控系统,也有监控小工具等等. Java 就已经自带了一些监控工具,可以不借助外部软件的情况下简单、快速查看相应监控信息.

关于JVM(JDK),Tomcat,Linux的最大线程数问题

- - 孟飞阳的博客
一、JVM(JDK)最大线程数. JVM最大创建线程数量由JVM堆内存大小、线程的Stack内存大小、系统最大可创建线程数(Java线程的实现是基于底层系统的线程机制来实现的,Windows下_beginthreadex,Linux下pthread_create)三个方面影响. -Xmx  最大堆内存.

常用的JDK自带命令行工具

- - 四火的唠叨
文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》. 在 这里可以找得到这些工具的列表,虽然官网上免责声明为 “The tools described in this section are unsupported and experimental in nature and should be used with that in mind.

[原]jdk自带检测内存,cpu,线程 的工具——jvisualvm

- - 玩转java
一个很强大的免费工具:JDK6自带的jvisualvm. 其实还有个自带工具 jconsole   这里不多说, 用法跟 jvisualvm 差不多. 这个东西我以前听过说,但一直没有用过. 看到它提供的截图中可以看到各线程的运行状态,所以准备试一下. 在命令行输入jvisualvm. 如果jdk安装正确的话(6.x以上版本),就会看到如下的一个窗口:.

[iteye]每个Java开发者都应该知道的5个JDK工具

- - PHP & Java
【编者按】JDK是Java语言的软件开发工具包,没有它就无法编译Java程序. 目前,有许许多多的JDK工具呈现在大家面前,但最常用的莫过于java.exe、javac.exe、jar等. 本文作者Joe拥有多年的Java开发经验,其在博客上分享了一篇文章: 5 JDK Tools Every Java Developer Should Know,笔者对其进行了编译,以下为译文.

Sun JDK 1.6内存管理

- 小丑鱼 - 淘宝JAVA中间件团队博客
分为使用篇、调优篇和实现篇三个部分,使用篇为填鸭式,调优篇为pattern式,实现篇为启发式,三个PPT的目标为:. 1.掌握Sun JDK的内存区域的划分;. 2.掌握Sun JDK垃圾收集器的使用方法和触发时机;. 4.掌握一些基本的GC调优的方法;. 5.了解自动内存管理的常见实现方法,以及Sun JDK所做的优化.

JDK动态代理机制

- - CSDN博客编程语言推荐文章
    代理模式有两种,一种是静态代理,这种方式需要为每一个被代理类写一个代理类,显示比较麻烦. 还一种是动态代理,动态代理实现方式一般有两种,JDK动态代理与CGLIB动态代理,这里说一下对JDK动态代理的理解.     JDK动态代理最核心的就类就是java.lang.reflect.Proxy,可调用Proxy.newInstance(..)生成动态代理.

JAVA之JDK动态代理

- - Java - 编程语言 - ITeye博客
在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和接口,可以生成JDK动态代理类或动态代理对象. Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类,如果在程序中为一个或多个接口动态的生成实现类,就可以使用Proxy来创建动态代理类,.

JVM问题定位工具

- - 四火的唠叨
文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》. JDB是基于文本和命令行的调试工具,Jikes在JDB的基础上提供了GUI. 熟悉JDB还是有价值的,很多情况下需要我们在命令行下完成简单的debug问题定位. 我们可能更熟悉使用下面这样的方式来进行调试,但本质上就是在使用JDB:.