最近看到了大量关于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目录来说的,因此避免找不到dump的文件,这里推荐使用绝对路径。此外,也建议使用.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       483****52  [B

2:        235****81       224****84  [C

3:        103958       16069448  <constMethodKlass>

4:        482****61       154****52  java.util.HashMap$Entry

5:        103958       141****80  <methodKlass>

6:          9576       111****68  <constantPoolKlass>

7:        186****64       10430784  com.mysql.jdbc.ConnectionPropertiesImpl$BooleanConnectionProperty

8:        274****09        877****88  java.util.Hashtable$Entry

9:          9576        721****52  <instanceKlassKlass>

10:          7972        6404256  <constantPoolCacheKlass>

11:        229****37        551****88  java.lang.String

12:         48471        542****52  java.net.SocksSocketImpl

13:         21599        385****72  [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锁。这俩种锁的不同之处主要在于前者是限制在进入synchronized部分的地方的栈帧(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自带工具之问题排查场景示例的更多相关文章

  1. [转]JDK自带工具之问题排查场景示例

    最近看到了大量关于java性能调优.故障排查的文章,自己也写了一篇Java调优经验谈.接着此篇文章,其实一直打算写写一些常用调优工具以及它们的惯常用法的.后来在http://java-performa ...

  2. JDK自带工具keytool生成ssl证书

    前言: 因为公司项目客户要求使用HTTPS的方式来保证数据的安全,所以木有办法研究了下怎么生成ssl证书来使用https以保证数据安全. 百度了不少资料,看到JAVA的JDK自带生成SSL证书的工具: ...

  3. 利用JDK自带工具监控JVMCPU和内存指标

    特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...

  4. jvm性能监控(3)-jdk自带工具 jps jstack jmap

    一.概要: jps -l 查看现有的java进程 jps -l 显示所有正在运行的java进程id   jstack 查看Java线程      jstack -l pid; 做thread dump ...

  5. JDK 自带工具试用(一)

    简述: 运维监控会用到JDK的小工具 说明: 1. jps 用来查看当前运行的Java进程 我在eclipse中起了一个web 应用 或者用jps -l 可以查看的更清楚一点 jps -v 看到103 ...

  6. JDK自带工具介绍

    JDK工具一览表 工具名称 功能描述 appletviewer.exe 用于运行并浏览applet小程序. apt.exe 注解处理工具(Annotation Processing Tool),主要用 ...

  7. 利用JDK自带工具keyTool生成安全证书

    前言:说一下最近做的工作,主要利用iText给网页中生成好的html报表转化为pdf格式的文件,并且在其中加入水印,数字签名等等,这部分主要介绍安全证书的目的就是为了做数字签名部分用的. 下面利用jd ...

  8. JDK自带工具列表

    JDK是一个功能强大的Java开发套装,它不仅仅为我们提供了Java运行环境,还给开发人员提供了许多有用的开发组件(位于bin目录中,如下图所示).仅仅使用JDK,就能够解决我们在Java开发过程中遇 ...

  9. JDK自带工具一览表。妈妈再也不用担心你到处去下载小软件了~~

    原来JDK早早就给我准备好了要用到的工具..反编译,JVM性能监视.诊断. JDK(Java Development Kit)是Java程序员最核心的开发工具,没有之一. JDK是一个功能强大的Jav ...

随机推荐

  1. 微信小程序 - 各种示例

    示例更新(后续持续更新): 最后一次更新:2018-11-7 小程序-地图显示(调用高德地图.腾讯App) - (2018-11-1) 小程序-上传图片(上传.预览.展示.删除) - (2018-11 ...

  2. 移动端兼容 - faskclick.js

    fasckclick为解决移动端300ms延迟而生 github地址为:https://github.com/ftlabs/fastclick 使用方法: 1. 原生使用(window.onload或 ...

  3. JDBC一(web基础学习笔记七)

    一.JDBC Java数据库的连接技术(Java DataBase Connectivity),能实现Java程序以各种数据库的访问 由一组使用Java语言编写的类和接口(JDBC API)组成,它j ...

  4. 各种样式的table 及 代码

    1.模板一 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...

  5. 用javascript的isNan()函数,可以判断是否为数字

    var getstockid = $.trim($("#SearchString").val()); if (!isNaN(getstockid)) alert('是数字!'); ...

  6. tomcat 热部署、热加载 精析

        1.前言 找了很多篇文章,没有一篇文章讲的清晰.明了,很多人只是会用,但不是能真正说明白,这年头找个懂理论的,真难! 2.热部署 原定义:tomcat处于运行状态时,能够监测webapps下的 ...

  7. python之函数用法basestring

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python之函数用法basestring #http://www.cnblogs.com/oneday/a ...

  8. DIV+CSS专题:十天学会DIV+CSS

    DIV+CSS专题:十天学会DIV+CSS,在网上看到的.感觉蛮好,推荐一下. 十天学会DIV+CSS(WEB标准)CHM格式文件下载 第十天 div+css网页标准布局实例教程(三) 第十天 div ...

  9. openvpn服务端一键生成windows,macos客户端配置文件

    #!/bin/bash #获取参数 while getopts "n:" opt; do case $opt in n) client_name=$OPTARG ;; \?) ;; ...

  10. 基于 html5 geolocation来获取经纬度地址(copy)

    geolocation来获取经纬度地址 以前如果要获取互联网用户所在地都是根据用户的IP地址来获取地理位置,这样获取到的数据和真实数据有很大的偏差.为了获取更加精确的位置,可以使用了html5的geo ...