个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈
引言
对于大型 java 应用程序来说,再精细的测试都难以堵住所有的漏洞,即便我们在测试阶段进行了大量卓有成效的工作,很多问题还是会在生产环境下暴露出来,并且很难在测试环境中进行重现。JVM 能够记录下问题发生时系统的运行状态并将其存储在转储(dump)文件中,从而为我们分析和诊断问题提供了重要的依据。常见的转储文件包括 Java Dump, Heap dump 和 System dump。这里我们主要介绍 Java dump 在 JVM 故障诊断中的应用。
Java dump,也叫做 Thread dump,是 JVM 故障诊断中最重要的转储文件之一。JVM 的许多问题都可以使用这个文件进行诊断,其中比较典型的包括线程阻塞,CPU 使用率过高,JVM Crash,堆内存不足,和类装载等问题。作为一款轻量级(与 Heap dump 和 System dump 相比)的转储文件,Java dump 的确是我们诊断 JVM 问题的首选。本文将系统的介绍使用 Java dump 进行 JVM 故障诊断的方法和技巧,希望能够为大家提供一些帮助。
Java dump 文件的格式和内容
Java dump 通常是文本格式(.txt),因此可以通过一般的文本编辑器进行阅读,阅读时需要注意段与行的格式:
段格式
为了便于大家的分析,Java dump 的每一段的开头,都会用“-----”与上一段明显的区分开来。而每一段的标题也会用“=====”作为标识,这样我们就能够很容易的找到每一段的开头和标题部分(如清单 1)。
清单 1. Java dump 段标题示例
NULL --------------------------------
0SECTION TITLE subcomponent dump routine
NULL ===============================
行格式
Java dump 文件中,每一行都包含一个标签,这个标签最多由 15 个字符组成(如清单2中所示)。其中第一位数字代表信息的详细级别(0,1,2,3,4),级别越高代表信息越详细;接下来的两个字符是段标题的缩写,比如,“CI” 代表 “Command-line interpreter”,“CL” 代表 “Class loader”,“LK” 代表 “Locking”,“ST” 代表 “Storage”,“TI” 代表 “Title”,“XE” 代表 “Execution engine”等等;其余部分为信息的概述。
清单 2. Java dump 行标签和内容示例
1TISIGINFO Dump Event "uncaught" (00008000) Detail "java/lang/OutOfMemoryError" received
不同版本的 JVM 所产生的 Java dump 的格式可能会稍有不同,但基本上都会包含以下几个方面的内容:
-
TITLE 信息块:描述 JAVA DUMP 产生的原因,时间以及文件的路径。
-
GPINFO信息块:GPF 信息。
-
ENVINFO 信息块:系统运行时的环境及 JVM 启动参数。
-
MEMINFO 信息块:内存的使用情况和垃圾回收记录。
-
LOCKS 信息块:用户监视器(Monitor)和系统监视器(Monitor)。
-
THREADS信息块:所有 java 线程的状态信息和执行堆栈。
-
CLASSES信息块:类加载信息。
利用 Java Dump 进行 JVM 故障诊断
由于 Java dump 文件包含的内容比较广泛,因此 JVM 的很多问题都可以通过 java dump进行诊断。这些问题主要包括线程阻塞,CPU 使用率过高,JVM Crash,堆内存不足,和类装载等问题。
诊断线程阻塞问题
线程阻塞是我们在 java 多线程编程中经常遇到的问题。由于对后端有限资源的争用以及过度同步等问题,经常会发现 Java dump 中某个资源(锁对象)下有太多的线程处于等待状态,这时候我们通常需要从以下三个方面去诊断这个问题:
-
这个锁存在的目的是什么?有没有可能去掉这个锁或者缩小这个锁保护的范围,从而减少线程等待问题发生的几率。
-
有哪些线程需要用到这个锁,有没有可能改用其它更好的替代方案。
-
当前哪个线程正在持有这个锁,持有的时间是多长,有没有可能缩短持有的时间。
比线程阻塞更严重的是死锁问题,当两个以上的线程互相等待对方的锁,从而形成一个环的时候,就会发生死锁。关于如何使用 Java dump 诊断死锁的问题,请参考 在 WebSphere Application Server V6.1 应用程序中跟踪死锁 一文,该文对此问题做了较为详细的介绍。
诊断 JVM Crash 问题
JVM Crash 是我们所碰到的最棘手的问题之一,它对整个系统的影响是致命的,并且总是让人防不胜防。导致 JVM 崩溃的原因有很多,通常都是一些底层的错误。比如 JVM 本身的 bug,错误的 JNI 调用,第三方原生模块(比如数据库驱动程序)中的 bug 等。JVM崩溃的原因复杂,并且大多都难以重现,所以诊断起来有一定的难度。
一般来说,JVM 崩溃的时候,系统一般会自动产生一个 Java dump 文件(JVM 默认的设置参数就会触发)。这个 Java dump 会帮我们记录下 JVM 崩溃的原因,相关的信息会记录在 TITLE 信息块,GPINFO 信息块和 THREADS 信息块中。