随着多核 CPU 的日益普及,越来越多的 Java 应用程序使用多线程并行计算来充分发挥整个系统的性能。多线程的使用也给应用程序开发人员带来了巨大的挑战,不正确地使用多线程可能造成线程死锁或资源竞争,导致系统瘫痪。因此,需要一种运行时线程监控工具来帮助开发人员诊断和跟踪 Java 线程状态的切换。JDK 1.5 及其后续版本提供了监控虚拟机运行状态的接口 JVMTI。
JVMTI 工具接口
随着多核 CPU 技术的发展,多线程编程技术被广泛地应用,从而充分发挥整个系统的性能。Java 语言对多线程编程提供了语言级的支持,可以方便地创建、运行、销毁线程。然而,多线程的使用也给应用程序开发人员带来了巨大的挑战,不正确地使用多线程可能造成线程死锁或资源竞争,导致系统瘫痪。
为了帮助 Java 开发人员诊断和跟踪 Java 线程状态的切换,Sun 公司在 Java 开发工具包(Java2 Software Development Kit, JDK)1.5.0 版本中引进了 Java 虚拟机工具接口(Java Virtual Machine Toolkit Interface,JVMTI),用于替代在先前的 JDK 版本中作为试验功能存在的 Java 虚拟机剖析接口(Java Virtual Machine Profiling Interface,JVMPI)和 Java 虚拟机调试接口(Java Virtual Machine Debugging Interface,JVMDI)。通过 JVMTI 接口可以创建代理程序(Agent)以监视和控制 Java 应用程序,包括剖析、调试、监控、分析线程等等,其架构模型如图 1 所示。
图 1. JVMTI 架构模型
Agent 可以向运行中的虚拟机实例订阅感兴趣的事件,当这些事件发生的时候,会以事件回调函数的方式激活代理程序,同时 JVMTI 提供了众多的功能函数,以查询和控制 Java 应用程序的运行状态。Agent 通过 JVMTI 所提供的接口与虚拟机进行通信,并同步监控虚拟机的运行状态,它与运行中的 Java 应用程序是相对独立的,不会干扰程序的正常运行。Agent 可以用任何支持 C 语言标准的本地语言来编写,并以动态链接库的方式存在;Java 程序启动的时候可以加载这个动态链接库。
基于 JVMTI 接口构建的 Agent 可以方便地实现对 Java 线程状态切换的跟踪,从而使开发人员能够在运行时清楚地了解多线程应用程序中线程的工作情况,方便进行调试和除错。本文后续部分将介绍如何基于 JVMTI 接口构建 Java 线程切换监控代理。
Java 线程模型
要对 Java 线程的切换进行监控,必须先了解 JVM 中的 Java 线程模型。Java 线程模型可以用图 2 所示的 Java 线程生命周期来描述。Java 线程的生命周期包括创建,就绪,运行,阻塞,死亡 5 个状态。一个 Java 线程总是处于这 5 个生命周期状态之一,并在一定条件下可以在不同状态之间进行转换 。
图 2. Java 线程模型
创建状态 (New Thread)
在 Java 语言中使用 new 操作符创建一个线程后,该线程仅仅是一个空对象,它具备了线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。
就绪状态 (Runnable)
使用 start() 方法启动一个线程后,系统为该线程分配了除 CPU 外的所需资源,使该线程处于就绪状态。此外,如果某个线程执行了 yield() 方法,那么该线程会被暂时剥夺 CPU 资源,重新进入就绪状态。