IBM Technology for Java(IT4J,又名 J9) 是 IBM 自己开发的 Java 虚拟机,被发布在 IBM 的各个操作系统上。IBM i 从 5.4 版本中开始引入 J9,从 6.1 开始,系统默认的 Java 虚拟机 是 J9,从 7.1 开始,C++lassic JVM 不再被支持,J9 是 IBM i 上唯一的支持的 Java 虚拟机。
在 IBM i 上 ,PASE(Portable Application Solution Environment) 提供了一个仿 AIX 的运行时环境,使得 AIX 或 其他 UNIX/Linux 应用程序只要做很少的改动甚至不修改就能够在 IBM i 上运行。J9 在 IBM i 上发布的核心代码是 AIX 版本的,所以它是在 PASE 环境中运行的。另外,J9 在 IBM i 上还有虚拟机的启动,停止与管理等功能。这些是通过 IBM i 的 CL 命令界面和少量的 ILE 支撑程序来实现的。所以与 Classic JVM 不同,在 IBM i 上调试与解决 J9 的问题需要从 ILE 和 PASE 两个环境同时入手分析。
J9 产生的几种日志
当运行在 J9 上的 Java 应用程序出现问题时,最有效的">分析问题方法是查看 J9 产生的各种日志。J9 通常会共产生 core dump, java core,snap trace 和 heap dump 四种日志。
Core dump 就是 Unix 或 Linux 系统上的内存转储文件,主要包括 J9 崩溃时的内存映像。
Java core 文件保存的是 java 应用程序在崩溃时或任一时刻关于 Java 运行环境的各种信息。包括 Java 虚拟机的参数,环境变量,内存段的分配情况,垃圾回收日志,各种内部锁的状态,各线程在当前时刻的运行栈,以及类加载状态等。
Snap trace 是 J9 记录 Java 虚拟机自身运行过程的日志,通常在 J9 崩溃时 Snap trace 中会包含崩溃前的部分日志。
Heap dump 文件是一个二进制文件,它保存了某一时刻在 Java 堆中所有对象的状态。这个文件最重要的作用就是分析 Java 堆内存泄露问题,heap analyzer,MAT 等工具都可以分析这种文件。
除了以上几种日志以外,当 J9 崩溃时,相应的会产生 IBM i 的日志:joblog 和 vlog。
导致 J9 崩溃的原因分析
Java 虚拟机非正常地停止运行可能是多种原因引起的,例如 Java 程序产生了无法处理的异常,虚拟机运行过程中产生不可恢复的错误,虚拟机所在的进程崩溃等。对于 Java 异常和虚拟机内部产生的错误,一般会有对应的错误消息指示发生了哪种问题,相对容易找到问题的根源。而对于虚拟机崩溃问题相对比较复杂。崩溃通常由以下几种原因产生:
在与 JNI 相关的本地代码中崩溃
JNI 是 Java 820.html">Native Interface 的缩写,即 JAVA 本地调用。从 Java1.1 开始,Java Native Interface(JNI) 标准成为 java 平台的一部分,它允许 Java 代码和其他语言写的代码进行交互。
本地代码在多种情况下都会用到 JNI。 而 JNI 的使用中有许多需要注意的问题(详见”使用 Java Native Interface 的最佳实践“)。这些本地代码对 JNI 不正确的使用往往会造成 J9 所在的 JOB 崩溃。使用 JNI 的方式包括:
Java 本地方法
在 IBM i 上一个 Java 方法可以用 ILE 或者 PASE 环境下的其他语言来实现。
使用 Invocation API
Invocation API 是 JNI 的一部分,它可以用来将 Java 虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用 Java 代码。同时也可以用其他语言通过 JNI 函数接口来调用 Java 代码。
JVMTI agent
JVMTI(JVM Tool Interface)是 Java 虚拟机所提供的本地编程接口,是 JVMPI(Java Virtual Machine Profiler Interface)和 JVMDI(Java Virtual Machine Debug Interface)的更新版本。JVMTI 提供了可用于 debug 和 profiler 的接口;同时也支持监听(Monitoring),线程分析(Thread analysis)以及覆盖率分析(Coverage Analysis)等功能。JVMTI 提供一套本地代码接口,因此使用 JVMTI 需要我们与 C/C++ 以及 JNI 打交道。开发时一般采用建立一个 Agent 的方式来使用 JVMTI,它使用 JVMTI 函数,设置一些回调函数,并从 Java 虚拟机中得到当前的运行态信息,并作出自己的判断,最后还可能操作虚拟机的运行态。把 Agent 编译成一个动态链接库之后,我们就可以在 Java 程序启动的时候来加载它(启动加载模式),也可以在 Java 5 之后使用运行时加载(活动加载模式)。