java程序运行时,有时会产生javacore及heapdump文件,java程序在遇到致命问题时,就会产生这两个文件,有时产生时,java应用不会死掉,还能继续运行,有时则java进程会死掉,即java进程被杀死。为了能够保留java应用发生致命错误前的java的运行状态,jvm在死掉前产生两个文件,分别为javacore及heapdump文件。
javacore文件主要保存的是java应用各线程在某一时刻的运行的位置,即执行到哪一个类的哪一个方法哪一个行上。javacore是一个文本文件,打开后可以看到每一个线程的执行栈,以stacktrace的方式显示。通过对javacore的分析可以得到应用是否“卡”在某一点上,即在某一点运行的时间太长,如数据库查询,长期得不到响应,最终导致系统崩溃。
heapdump文件是一个二进制文件,它保存了某一时刻jvm堆中对象情况,这种文件需要相应的工具进行分析,笔者用得较多的是heap analyzer这个工具。这个文件最重要的作用就是分析系统是否存在内存溢出的情况,通过heapanalyzer可以很简单地分析出溢出的位置。
这两个文件也可以手工的方式生成,经常我们会遇到系统变慢或无响应的情况,这个时候就以采用手工的方式生成javacore及heapdump文件,通过对这两个文件的分析,查出原因进而解决问题,在unix/linux上,产生这两个文件的方法是首先,ps -ef|grep java 出java进程id ,然
后再执行kill -3 进程号的操作,等文件生成后再做一次同样的操作,再产生一组文件,两组文件在分析javacore时特别有效,困为它可以看出在先后两个时间点上,线程执行的位置,如果发现先后两组数据中同一线程都执行在同一位置,则说明此处可能有问题,因为程序运行是极快的,如果两次均在某一点上,说明这一点耗时是很大的。。
1. Java DUMP分析   
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 信息块中。
TITLE 信息块:用于确认问题产生的原因,即是否是由于一些底层错误而导致 JVM Crash。
GPINFO 信息块:用于查看问题的详细信息和问题定位。
THREADS信息块:用于了解问题线程的运行情况。
下面我们通过一个具体的例子来介绍 JVM Crash 问题的诊断方法。TestJni是一个简单的 Java 应用,它通过 JNI 调用本地代码 CallJin.dll 中的doSomeThing() 函数。

清单 3. 在TestJni类中调用 CallJin.dll 中的函数
package test;
public class TestJin {
    /**
    * @paramargs
    */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
TestJintestObj = new TestJin();
testObj.work();
    }
    public void work() {
CallJni.doSomeThing();
    }
}
package test;
public class CallJni {
    static
    {
System.loadLibrary("CallJni");
System.out.println("Dll Loaded");
    }
    public native static void doSomeThing();
}

CallJin.dll 是 C++ 编写得本地库,其源代码如清单 3 所示:

清单 4. CallJni.dll 的 C++ 实现代码
#include <com_test_CallJni.h>
/*
* Class:    com_test_CallJni
* Method:    doSomeThing
*/
JNIEXPORT void JNICALL Java_test_CallJni_doSomeThing
  (JNIEnv *, jclass){
int *i;
      *i = 100;
java源码阅读工具  }

在这段 C++ 代码中,整形指针 I 还没有分配空间就被赋了值,这是一个非常严重的错误。当然 java 应用程序员并不知道这一点,并且在 java 应用程序中调用了doSomeThing()这个 JNI 函数。结果导致 JVM 发生了崩溃。
在这段 C++ 代码中,整形指针 I 还没有分配空间就被赋了值,这是一个非常严重的错误。当然 java 应用程序员并不知道这一点,并且在 java 应用程序中调用了doSomeThing()这个 JNI 函数。结果导致 JVM 发生了崩溃。
下面是 JVM 崩溃时,系统为我们生成的 Java dump 文件的片断。

清单5. Java dump 文件片断
NULL          ----------------------------------------------
0SECTION      TITLE subcomponent dump routine
NULL          ===============================
1TISIGINFO    Dump Event "gpf" (00002000) received
1TIDATETIME    Date:                2008/11/12 at 20:45:24
1TIFILENAME Javacore filename:
C:\eclipse\workspace\Serviceability\TestApps\SampleLeak\TestJin\
javacore.20081112.