Java基础故障处理工具

发布时间 2023-12-04 22:28:41作者: cac2020

给一个系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。 这里说的数据包括但不限于异常堆栈、 虚拟机运行日志、 垃圾收集器日志、 线程快照(threaddump/javacore文件)、堆转储快照(heapdump/hprof文件) 等。恰当地使用虚拟机故障处理、分析的工具可以提升我们分析数据、 定位并解决问题的效率。

      这些故障处理工具并不单纯是被Oracle公司作为“礼物”附赠给JDK的使用者, 根据软件可用性和授权的不同, 可以把它们划分成三类

(1)商业授权工具: 主要是JMC(Java Mission Control) 及它要使用到的JFR(Java FlightRecorder) , JMC这个原本来自于JRockit的运维监控套件从JDK 7 Update 40开始就被集成到OracleJDK中, JDK 11之前都无须独立下载, 但是在商业环境中使用它则是要付费的。

(2)正式支持工具: 这一类工具属于被长期支持的工具, 不同平台、 不同版本的JDK之间, 这类工具可能会略有差异, 但是不会出现某一个工具突然消失的情况。

(3)实验性工具: 这一类工具在它们的使用说明中被声明为“没有技术支持, 并且是实验性质的”(Unsupported and Experimental) 产品, 日后可能会转正, 也可能会在某个JDK版本中无声无息地消失。 但事实上它们通常都非常稳定而且功能强大, 也能在处理应用程序性能问题、 定位故障时发挥很大的作用。

备注:JDK开发团队选择采用Java语言本身来实现这些故障处理工具,这些命令行工具大多仅是一层薄包装而已,真正的功能代码是实现在JDK的工具类库中的。在JDK9前,这些代码实现在jdk\lib\tools.jar,JDK9模块化改造之后放在jdk\jmods(自从Jdk9之后,Java语言提供了模块化系统,允许发布的Java软件只加载必要的模块,JMOD就是为此引入的新格式。例如jmods目录的各个jmod文件用7z、rar等压缩工具打开)目录下。为啥使用java语言来实现是有特别用意的:当应用程序部署到生产环境后, 无论是人工物理接触到服务器还是远程Telnet到服务器上都可能会受到限制。 借助这些工具类库里面的接口和实现代码, 开发者可以选择直接在应用程序中提供功能强大的监控分析功能。

如果读者在工作中需要监控运行于JDK 5的虚拟机之上的程序, 在程序启动时请添加参数“-Dcom.sun.management.jmxremote”开启JMX管理功能, 否则由于大部分工具都是基于或者要用到JMX(包括下一节的可视化工具),它们都将无法使用, 如果被监控程序运行于JDK 6或以上版本的虚拟机之上, 那JMX管理默认是开启的, 虚拟机启动时无须再添加任何参数。

1、jps:虚拟机进程状况工具(JVM Process Status Tool)

名称和功能和ps类似,列出正在运行的虚拟机进程,并显示虚拟机执行主类( Main Class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID( LVMID, Local Virtual Machine Identifier)。本地虚拟机进程来说,LVMID与操作系统的进程ID(PID,Process Identifier)是一致的。

命令格式

jps [-q] [-mlvV] [<hostid>],

源码

tools.jar:    sun.tools.jps.Jps

-help或-h

列出命令帮助信息

-q

只输出LVMID,省略主类的名称

-m

输出虚拟机进程启动时传递给主类main()函数的参数

-l

输出主类的全名,如果进程执行的是JAR包,则输出JAR路径

-v

输出虚拟机进程启动时的JVM参数

2、jstat:虚拟机统计信息监视工具(JVM Statistics Monitoring Tool)

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

命令格式:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]  通过jstat -help查看当前jdk版本支持的格式

源码:tools.jar:sun.tools.jstat.Jstat

-help或-h

列出命令帮助信息

-option

用户希望查询的虚拟机信息, 主要分为三类: 类加载、 垃圾收集、 运行期编译状况。可以使用命令:jstat -options列出所有查询类型

类加载

-class

监视类加载、卸载数量、总空间以及类装载所耗费的时间

垃圾收集

-gc

监视Java堆状况,包括Eden区、2个Surviver区、老年代、永久代等的容量,已用空间,垃圾收集时间合计等信息

-gccapacity

监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间

-gcutil

监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比

-gccause

监视内容与-gc基本相同,但是会额外输出导致上一次垃圾收集产生的原因

-gcnew

监视新生代垃圾收集状况

-gcnewcapacity

监视内容与-gcnew基本相同,但输出主要关注使用到的最大、最小空间

-gcold

监视老年代垃圾收集状况

-gcoldcapacity

监视内容与-gcold基本相同,但输出主要关注使用到的最大、最小空间

-gcpermcapacity

输出永久代使用到的最大、最小空间

运行期编译状况

-compiler

输出即时编译器编译过的方法、耗时等信息

-printcompilation

输出已经被即时编译的方法

-t

把时间戳列显示为输出的第一列。这个时间戳是从Java虚拟机的开始运行到现在的秒数。

-h<lines>

每显示lines行显示一次表头,其中lines为正整数。默认值为 0,即仅在第一行数据显示一次表头。例:-h5 每5行打印一次行头

vmid

java进程ID

interval

显示信息的时间间隔,单位默认毫秒。也可以指定秒为单位,比如:1s。如果指定了该参数,jstat命令将每个这段时间显示一次统计信息。例:jstat -gc 32586 1000 等价于 jstat -gc 32586 1s

count

显示数据的次数,默认值是无穷大,这将导致jstat命令一直显示统计信息,直到目标JVM终止或jstat命令终止

3、jinfo: Java配置信息工具(Configuration Info for Java)

实时查看和调整虚拟机各项参数。jinfo可以在运行时修改部分参数并使之立即生效,但是并非所有参数都支持动态修改,只有被标记manageable可以被实时修改(通过命令java -XX:+PrintFlagsFinal | grep manageable 查看),这个修改能力是极其有限的。

使用jps命令的-v参数可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定的参数的系统默认值,有两种方式:

(1) 使用jinfo的-flag选项进行查询

(2) JDK 6或以上版本,使用java -XX:+PrintFlagsFinal查看

命令格式

jinfo [option] <pid>

源码

tools.jar:sun.tools.jinfo.JInfo

-help或-h

查看帮助信息

-flag <name>

查看JVM里参数名为name的值

比如在生产环境在不停应用前提下开启如下参数:发生OOM的时候自动生成dump堆快照和打印GC日志

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/oom.hprof -XX:+PrintGCDetails

-flag [+|-]<name>

+启用或-停用JVM里参数名为name功能

-flag <name>=<value>

给名为name的JVM参数赋值

-flags

查看JVM各个flags信息

-sysprops

把虚拟机进程的System.getProperties()的内容打印出来

<no option>

不加option参数,例如:jinfo pid,会将-flags和-sysprops参数对应信息一起输出

4、jmap:Java内存映像工具( Memory Map for Java)

用于生成堆转储快照(一般称为heapdump或dump文件)。jmap的作用并不仅仅是为了获取堆转储快照,它还可以查询finalize执行队列、 Java堆和方法区的详细信息, 如空间使用率、当前用的是哪种收集器等。和jinfo命令一样,jmap有部分功能在Windows平台下是受限的,除了生成堆转储快照的-dump选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统中都可以使用之外,其余选项都只能在Linux/Solaris中使用。

如果不使用jmap命令,要想获取Java堆转储快照也还有一些比较“暴力”的手段:

(1)譬如使用-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在内存溢出异常出现之后自动生成堆转储快照文件;

(2)通过-XX:+HeapDumpOnCtrlBreak参数则可以使用[Ctrl]+[Break]键让虚拟机生成堆转储快照文件;

(3)在Linux系统下通过Kill -3命令发送进程退出信号“恐吓”一下虚拟机,也能顺利拿到堆转储快照。

命令格式

jmap [option] <pid>

源码

tools.jar:sun.tools.jmap.JMap

-help或-h

查看帮助信息

-dump

生成Java堆转储快照。格式为-dump:[live,]format=b,file=<filename>,其中live子参数说明是否只dump出存活的对象

-histo

格式:-histo:[live]显示堆中对象统计信息,包装类、实例数量、合计容量, 其中live子参数说明是否只统计存活的对象

-F

当虚拟机进程对-dump、-histo选项没有响应时,可使用-F强制生成dump快照、histo统计信息,这个模式下live参数无效。只在Linux\Solaris平台下有效。

-heap

显示Java堆详细信息,如使用哪种垃圾回收器、参数配置、分代状况等。只在Linux\Solaris平台下有效。

-permstat

以ClassLoader为统计口径显示永久代内存状态。只在Linux\Solaris平台下有效。

-finalizerinfo

显示在FQueue中等待Finalizer线程执行finalize方法的对象。只在Linux\Solaris平台下有效。

-clstats

类加载器实例统计

-J<flag>

通过-J指定运行jmap的JVM参数<flag>。因为这些工具命令时开启一个java进程来运行,所以可以指定-J<flag>来控制工具进程的JVM运行参数,比如:jmap -J-d64 -heap 549,指定jmap进程的在64位JVM上运行, jmap -J-XX:+PrintGC -heap 549 开启打印GC参数等

5、jhat:虚拟机堆转储快照分析工具(JVM Heap Analysis Tool)

jhat命令与jmap搭配使用,来分析jmap生成的堆转储快照。jhat内置了一个微型的HTTP/Web服务器,生成堆转储快照的分析结果后,可以在浏览器中查看,默认端口7000。 源码:tools.jar:com.sun.tools.hat.Main

但是在实际工作中,一般不用jhat,主要原因有两个方面:一是一般不会在部署应用程序的服务器上直接分析堆转储快照,即使可以这样做,也会尽量将堆转储快照文件复制到其他机器上进行分析, 因为分析工作是一个耗时而且极为耗费硬件资源的过程,既然都要在其他机器上进行,就没有必要再受命令行工具的限制了。另外一个原因是jhat的分析功能相对来说比较简陋,后文将会介绍到的VisualVM,以及专业用于分析堆转储快照文件的Eclipse Memory Analyzer-MAT、IBM HeapAnalyzer(用于分析IBM J9虚拟机生成的映像文件,各个虚拟机产生的映像文件格式并不一致,所以分析工具也不能通用)等工具,都能实现比jhat更强大专业的分析功能。

#第一步 导出堆

[root@wjy ~]$ jmap -dump:live,file=b.bin 81301

Dumping heap to /home/wjy/b.bin ...

Heap dump file created

#第二步:分析堆文件

[root@wjy ~]$ jhat test.bin

Dump file created Sat Dec 02 20:50:03 CST 2023

Snapshot read, resolving...

Resolving 76633 objects...

Chasing references, expect 15 dots...............

Eliminating duplicate references...............

Snapshot resolved.

Started HTTP server on port 7000

Server is ready.

显示Server is ready.的提示后,用户在浏览器中输入http://localhost:7000/可以看到分析结果。

 

 

All classes including platform

显示出堆中所包含的所有的类

Show all members of the rootset

从根集能引用到的对象

Show instance counts for all classes (including platform)

显示平台包括的所有类的实例数量

Show instance counts for all classes (excluding platform)

显示平台外的所有对象信息

Show heap histogram

堆实例的分布表(堆直方图), 默认以包为单位进行分组显示,分析内存泄漏问题主要会使用到其中的“Heap Histogram”(与jmap -histo功能一样), 可以找到内存中总容量最大的对象

Show finalizer summary

显示析构器汇总信息

Execute Object Query Language (OQL) query

执行对象查询语句, 使用标准的对象查询语言 类似SQL的语法对内存中的对象进行查询统计

6、jstack:Java堆栈跟踪工具(Stack Trace for Java)

用于生成虚拟机当前时刻的线程快照(一般称为threaddump或javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。线程出现停顿时通过jstack来查看各个线程的调用堆栈,就可以获知没有响应的线程到底在后台做些什么事情, 或者等待着什么资源。

命令格式

jstack [option] <pid>

源码

tools.jar:sun.tools.jstack.JStack

-help或-h

查看帮助信息

-F

当正常输出的请求不被响应时,强制输出线程堆栈

-l

除堆栈外,显示关于锁的附加信息

-m

如果调用本地方法的话,可以显示C/C++的堆栈

从JDK 5起, java.lang.Thread类新增了一个getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。 使用这个方法可以通过简单的几行代码完成jstack的大部分功能, 在实际项目中不妨调用这个方法做个管理员页面, 可以随时使用浏览器来查看线程堆栈,下面是对应jsp页面代码:

<%@ page import="java.util.Map">

 

<html>

<head>

<title>服务器线程信息</title>

</head>

<body>

<pre>

<%

      for (Map.Entry<Thread, StackTraceElement[]> trace : Thread.getAllStackTraces().entrySet()){

            Thread thread = (Thread)trace.getKey();

            StackTraceElement[] stack = (StackTraceElement[]) trace.getValue();

            if (thread.equals(Thread.currentThread())) {

                continue;

            }

            out.print("\n线程: " + thread.getName() + "\n");

            for (StackTraceElement element : stack) {

                out.print("\t"+element+"\n");

            }

        }

%>

</pre>

</body>

</html>

非jsp实现:https://www.cnblogs.com/cac2020/p/16737269.html