用于定位java程序的瓶颈的方法和设备的制作方法

文档序号:6600967阅读:260来源:国知局
专利名称:用于定位java程序的瓶颈的方法和设备的制作方法
技术领域
本发明一般涉及计算机领域。更具体地说,本发明涉及一种用于检测并定位Java 程序的瓶颈的方法和设备。
背景技术
Java语言是是一种可以编写跨平台应用软件的面向对象的程序设计语言。Java 不同于一般的编译执行计算机语言(诸如C语言)和解释执行计算机语言(诸如HTML)。 它首先将源代码编译成字节码(bytecode),然后依赖各种不同平台上的Java虚拟机(JVM) 来解释执行字节码,从而实现了 “一次编译、到处执行”的跨平台特性。如今,Java已经成为企业应用程序的主流开发语言,理解Java线程如何工作是非常重要的。尤其当一个企业应用程序无法良好地利用下层的硬件服务器时,就需要找出在 CPU利用率低的情况下一些应用程序线程仍然被阻塞的原因。不过,Java应用程序具有如下性质在硬件和应用程序代码之间存在许多层,这些层包括但不限于硬件层、操作系统层 (也称作本地层)、Java虚拟机层、中间件层以及应用程序层。由于上述的原因,如果发现CPU的利用率低,难以在Java源代码中找到问题所在。 但是应用程序开发者恰恰需要定位Java源代码中的问题从而能够解决该问题。例如,图IA示出了 JVM层的线程状态,我们发现很多应用程序线程在Java虚拟机层是可运行的,并且在Java虚拟机层不存在明显的问题。但是,图IB示出了本地层中的、 与JVM层的线程对应的线程的状态,如图IB所示,存在许多的线程阻塞。因此,Java应用程序开发者需要弄清楚在CPU利用率低的情况下线程为什么被阻塞以及源代码中的哪个地方导致了发生该问题。在现有技术中,已经存在很多监视及瓶颈分析工具。下面举例说明现有技术中存在的各种工具。例如,存在一种本地瓶颈分析工具,该瓶颈分析工具查看执行栈中的一层以定位瓶颈。这种本地层瓶颈分析工具例如包括LockStat,该LockStat提供针对锁的统计结果。 这种瓶颈分析工具的缺点在于对于例如锁的每种资源,均需要一种专用的工具,这导致对于本地层的多种资源,需要很多的专用工具对每种资源进行监视和分析。此外,这种瓶颈分析工具只能在本地层即操作系统层中进行监视,而无法将在本地层中发生的事件与Java 源代码的相应部分联系起来。此外,存在一种筛选工具,该筛选工具跨越多层架构的各层进行查找以定位可疑的瓶颈。这种筛选工具主要用于多层架构的Web开发框架,其示例为哥伦比亚大学Watson 实验室的WAIT。所述多层架构典型地包括Web层、应用层以及数据库层。这种筛选工具仅能在该多层框架结构中识别引起可以瓶颈的节点(即硬件服务器)。因此,这种筛选工具识别的是包含多个节点的系统中导致瓶颈的节点,但是并不能在源代码中定位瓶颈。此外,目前还存在Java运行时监视工具,诸如jstack和JFluid。该jstack工具可以进行运行时栈分析,但是这种工具具有显著的性能开销,甚至会干扰应用程序的行为。而JFluid工具监控与特定资源相关的所有函数调用,这种工具同样具有显著的性能开销,因为即使有些函数调用与线程阻塞并无关联,JFluid仍会记录所有的函数调用。此外,jstack 和JFluid属于JVM层的监控工具,它们对JVM层中的瓶颈进行监视,但是无法监视位于JVM 层之下的本地层中的线程状 态。

发明内容
现有技术的各种工具都不能实现根据本地层中的瓶颈找出Java源代码中引起该瓶颈的精确位置的功能。因此,需要提供一种能够将本地层中的瓶颈链接回Java源代码的有效方法。为了解决上述问题,本发明的主要目的是提供一种用于检测并定位Java程序的瓶颈的方法和设备。该方法和设备能够将本地层中的瓶颈链接回Java源代码。此外,该方法和设备没有明显的性能开销,不会对目标应用程序的正常运行产生不利影响。根据本发明的一方面,提供了一种用于定位Java程序的瓶颈的方法,包括以下步骤在对应于所述Java程序的Java进程中创建辅助线程,并将所述辅助线程挂接到在该Java进程中创建的Java虚拟机;在操作系统内核中插入探测器;所述探测器监视所述 Java进程中的Java线程在操作系统内核中的状态并且响应于检测到Java线程被阻塞而向所述辅助线程发送信号;以及所述辅助线程响应于接收到来自操作系统内核的所述信号, 从所述JVM中取回调用栈信息,并利用所取回的调用栈信息定位到所述Java程序的源代码中的引起所述阻塞位置。根据本发明的另一方面,提供了一种用于定位Java程序的瓶颈的设备,包括用于在对应于所述Java程序的Java进程中创建辅助线程,并将所述辅助线程挂接到在该 Java进程中创建的Java虚拟机的装置;用于在操作系统内核中插入探测器的装置;用于由所述探测器监视所述Java进程中的Java线程在操作系统内核中的状态并且响应于检测到 Java线程被阻塞而向所述辅助线程发送信号的装置;以及用于在所述辅助线程响应于接收到来自操作系统内核的所述信号从所述JVM中取回调用栈信息,并利用所取回的调用栈信息定位到所述Java程序的源代码中的引起所述阻塞的位置的装置。采用本发明的上述设备和方法,能够将本地层中的瓶颈准确地链接回Java源代码,即找到引起所述本地层中的瓶颈的Java源代码的相应位置。因此,上述设备和方法能够在JVM层中没有任何指示的情况下,找到Java线程状态改变的原因。此外,上述方法是独立且自足的方法,不需要其它的监视器或工具的帮助。另外,上述设备和方法没有明显的性能开销,不会对目标应用程序的正常运行产生不利影响。


以下通过结合附图阅读参考下述对说明性实施例的详细描述,将更好地理解本发明本身、实施方式、其它目的及其优点。在附图中图IA和图IB示出了 JVM层的线程状态和本地层的线程状态之间的差别;图2是示出本发明的总体发明构思的示意图;图3示出了根据本发明的一个实施例的方法流程;图4是示出了用户空间中的Java线程和内核空间中的本地任务之间的关系的示意图;图5是示出了图3中的步骤320的处理的一个示例的示意图;图6是示出了图3中的步骤340的处理的一个示例的示意图;以及图7是示出了在四核处理器的情况下的辅助线程示例的示意图。现在参照附图描述优选方法和系统,其中,在附图中相同的附图标号用来指相同的部件。在下面的描述中,为了解释的目的,阐述大量特定的细节,以便帮助完全了解系统及方法等。在其它的例子中,为了简化描述,以框图的形式示出常用的结构和装置。对于本领域技术人员来说,可以想到很多修改和其它实施例,同时拥有在说明书和附图中所教导的益处。因此,应该理解,本发明不局限于所公开的特定实施例,另外可选的实施例应当包含在本发明的范围和范例发明构思内。虽然本文采用了一些特定术语,但是仅仅为了一般的描述意义而非限制目的使用它们。
具体实施例方式以下将参照附图对本发明的具体实施方式
进行详细说明。在以下的说明中,术语 “内核空间”和“用户空间”是针对操作系统的内核而言的。在本发明中,操作系统可以是诸如Unix、Linux以及Windows的各种操作系统。为了简单起见,在本发明中,仅以Linux作为操作系统的示例。但是本领域的技术人员应该明白,本发明的方法和设备同样适用于其它操作系统。Linux的虚拟地址空间为0至4G。Linux内核将这4G字节的空间分为两部分。将最高的IG字节(从虚拟地址OxCOOOOOOO到OxFFFFFFFF)供内核使用,称为“内核空间”。 而将较低的3G字节(从虚拟地址0x00000000到OxBFFFFFFF)供各个进程使用,称为“用户空间”。因为每个进程可以通过系统调度进入内核,因此,Linux内核由系统内的所有进程共享。内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。图2是示出本发明的总体发明构思的示意图。在本发明中,在作为监视目标的 Java进程中创建辅助线程,并且在操作系统的调度器中插入探测器。当该探测器检测到该 Java进程中的线程被阻塞时,向所述辅助线程发送用户定义的信号,接收到该用户定义的信号的辅助线程到JVM的栈中取回此时的调用栈信息,从而可以定位到Java源代码中的精确位置。这样就实现了将本地层中的瓶颈准确地链接回Java源代码。参照图3,本发明提供了一种用于检测并定位Java程序的瓶颈的方法。图3示出了根据本发明的一个实施例的方法流程300,包括如下步骤步骤310 创建辅助线程,并将其挂接到JVM。步骤320 在操作系统内核中插入探测器。步骤330 探测器监视Java线程并且在Java线程被阻塞时向辅助线程发送信号。步骤340 辅助线程接收信号,从JVM中取回调用栈信息并利用该信息定位到Java 源代码中的对应位置。这里需要说明的是,Java程序在被运行时表现为用户空间中的进程。JVM对应于一个独立运行的Java程序,即对应于一个Java进程。当启动一个Java程序时,一个JVM 实例就被启动起来了,任何一个拥有public static void main (String[]args)函数的类都可以作为Java程序运行的起点在JVM上运行。下面详细描述本发明的方法流程300中的各个步骤。步骤310 创津辅助线稈,并将其梓接到TVM 在步骤310中,在对应于所述Java程序的Java进程中创建辅助线程,并将所述辅助线程挂接到在该Java进程中创建的Java虚拟机。例如,可以通过Java虚拟机工具接口(JVMTI)提供的回调机制来创建辅助线程, 并且通过Java本地接口(JNI)提供的方法将创建的辅助线程挂接到JVM上。JVMTI可以用来监控JVM的一些行为。JNI是为了扩展Java标准类库以使之支持依赖于平台的特性而提供的接口。JNI接口允许以较低级的语言实现代码的一部分,然后令Java应用程序来调用这些以较低级语言编写的函数。具体地说,在JVM启动初始化完成的位置设置回调函数。例如,利用JVMTI,通过以下代码来启动响应虚拟机初始化事件的回调函数机制。jvmtiEventCallbacks callbacks ;//声明memset(&callbacks,0, sizeof (callbacks)) ;//初始化callbacks. VMInit = &vmlnit ;//编写的回调函数的入口jvmti- > SetEventCallbacks (&callbacks,sizeof (callbacks)) ;// 完成设置jvmti- > SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL) ;//启用对虚拟机初始化事件的通知上述代码的功能是把程序员自己编写的回调函数vmlnitO的地址赋值给 jvmtiEventCalIbacks类型的callbacks结构的变量VMInit,该变量表示在虚拟机初始化事件发生时调用的回调函数的入口。通过调用SetEventCalIbacksO方法完成设置,并且通过调用SetEventNotificationModeO方法启动对虚拟机初始化事件的通知,完成了回调函数vmlnitO的设置。这样,当虚拟机初始化时,回调函数vmlnitO就会被执行。要注意的是,为了便于说明,在本文中,不对公知方法或函数的参数进行描述,例如只简单将其表示为function ()。而对于用户自定义的函数,由于其参数可以由用户任意定义,因此也省略对这种函数的参数的定义和描述。本领域技术人员通过这种描述完全能够知道如何实现本发明的方法。在回调函数vmlnit ()中,调用JVMTI的RunAgentThread ()方法创建新的辅助线程。这里,需要说明的是,在一个创建了 JVM的进程中,并不是所有线程都能够直接使用JVM。为了区别于创建的辅助线程,Java进程中的对应于Java应用程序的线程在本文中被称为“Java应用线程”,而Java应用线程和辅助线程统称为Java线程。Java应用线程能够直接访问JVM,而辅助线程不能直接访问JVM。这就需要通过JOT接口提供的 AttachCurrentThreadO方法将当前的辅助线程挂接(attach)到JVM环境上。进行上述挂接的目的是为了使辅助线程能够实现对JVM中的栈的访问。为了使辅助线程能够对线程阻塞事件进行快速反应,需要将辅助线程设置为高的调度优先级。在描述步骤320之前,需要对用户空间中的Java线程和内核空间中的对应线程 (这里称为本地任务(native task))之间的关系进行描述。Java线程的调用栈位于用户空间中的JVM内,而本地任务的调用栈位于内核空间中。当一个Java进程通过系统调度进入内核时,其Java线程在内核中对应于一个本地任务,该本地任务被内核的调度器调度进入处理器而执行。当一个Java进程存在多个Java应用线程时,这些Java应用线程分别对应于内核中的一个本地任务,并且在上述的步骤310中创建的辅助线程同样对应于内核中的一个本地任务。如图4所示。图4是示出了用户空间中的Java线程和内核空间中的本地任务之间的关系的示意图。在图4中,示例性地示出了三个Java应用线程以及创建的辅助线程。 Java应用线程1至Java应用线程1分别对应于本地任务1至本地任务3,辅助线程对应于本地任务4。Java线程在用户空间中通过Java线程ID进行识别,但是本地任务在内核空间中通过本地任务ID进行识别。此外,在JVM中有每个Java线程的对应栈。当在内核空间中检测到一个本地任务(例如本地任务2)被阻塞时,需要知道在用户空间中与之对应的 Java线程(例如Java应用线程2),从而能够访问该Java线程的在JVM中的调用栈。为了实现上述目的,可以在每个Java线程启动时,通过回调函数建立该Java线程和操作系统内核中的对应于该Java线程的本地任务之间的映射关系。具体地说,与步骤 310类似,在JVM启动时设置 回调函数。例如,利用JVMTI,通过以下代码来启动响应线程启动事件的回调函数机制。jvmtiEventCallbacks callbacks ;//声明memset(&callbacks,0, sizeof (callbacks)) ;//初始化callbacks. ThreadStart = &threadStart ;// 编写的回调函数的入口jvmti- > SetEventCallbacks (&callbacks,sizeof (callbacks)) ;// 完成设置jvmti- > SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_THREAD_ START, NULL) ;//启用对线程启动事件的通知上述代码的功能是把程序员自己编写的回调函数threadStart ()的地址赋值给 jvmtiEventCal lbacks类型的cal Ibacks结构的变量ThreadStart,该变量表示在线程启动事件发生时调用的回调函数的入口。通过调用SetEventCalIbacksO方法完成设置,并且通过调用SetEventNotificationModeO方法启动对线程启动事件的通知,完成了回调函数threadStart ()的设置。这样,当Java线程启动时,回调函数threadStart ()就会被执行。在回调函数threadStart ()中,首先调用操作系统内核提供的系统调用函数,例如gettidO,获得当前Java线程的在内核空间中对应的本地任务的ID,即本地任务ID。然后,再调用JNI提供的机制,获得当前线程在JVM中的ID,即Java线程ID。然后,将获得的本地任务ID和Java线程ID相关联地存储到如图4所示的映射数据库中。通过上述方式, 每当有线程启动时,该线程都会调用回调函数threadStart ()将其在用户空间的Java线程 ID与其在内核空间的本地任务ID之间的映射关系存储起来。下面的表1示出了在图4的情况下建立的映射关系的可能的例子。表 1
本地任务ID Java线程ID 图4中的对应线程 5893 应用线程权利要求
1.一种用于定位Java程序的瓶颈的方法,包括以下步骤在对应于所述Java程序的Java进程中创建辅助线程,并将所述辅助线程挂接到在该 Java进程中创建的Java虚拟机JVM ;在操作系统内核中插入探测器;所述探测器监视所述Java进程中的Java线程在操作系统内核中的状态并且响应于检测到Java线程被阻塞而向所述辅助线程发送信号;以及所述辅助线程响应于接收到来自操作系统内核的所述信号,从所述JVM中取回调用栈信息,并利用所取回的调用栈信息定位到所述Java程序的源代码中的引起所述阻塞的位置。
2.根据权利要求1所述的方法,其中,在执行所述Java程序的处理器是多核处理器的情况下,创建多个所述辅助线程。
3.根据权利要求2所述的方法,其中,创建的多个所述辅助线程的数目与多核处理器的核数目相同。
4.根据权利要求3所述的方法,其中,创建的多个所述辅助线程中的每一个被分别绑定到多核处理器的一个核。
5.根据权利要求1所述的方法,还包括响应于每个Java线程的启动,通过回调函数建立该Java线程和操作系统内核中的对应于该Java线程的本地任务之间的映射关系。
6.根据权利要求5所述的方法,其中,所述信号包含被阻塞的本地任务的ID,并且其中,从所述JVM中取回调用栈信息包括根据本地任务的ID和所述映射关系,从所述JVM中取回与该本地任务对应的Java线程的调用栈信息。
7.根据权利要求1所述的方法,其中,所述探测器被插入在操作系统的调度器中并且在发生任务上下文切换的情况下执行操作。
8.根据权利要求7所述的方法,其中,所述探测器是被加载到操作系统内核中的用户自定义的模块插入到所述调度器中的。
9.根据权利要求7或8所述的方法,其中,响应于检测到Java线程被阻塞而向所述辅助线程发送信号包括如果在处理器执行任务上下文切换的情况下从处理器调出的本地任务对应于所述Java进程中的Java线程并且该本地任务处于阻塞状态,则由所述探测器向所述辅助线程发送信号。
10.一种用于定位Java程序的瓶颈的设备,包括用于在对应于所述Java程序的Java进程中创建辅助线程,并将所述辅助线程挂接到在该Java进程中创建的Java虚拟机JVM的装置;用于在操作系统内核中插入探测器的装置;用于由所述探测器监视所述Java进程中的Java线程在操作系统内核中的状态并且响应于检测到Java线程被阻塞而向所述辅助线程发送信号的装置;以及用于在所述辅助线程响应于接收到来自操作系统内核的所述信号从所述JVM中取回调用栈信息,并利用所取回的调用栈信息定位到所述Java程序的源代码中的引起所述阻塞的位置的装置。
11.根据权利要求10所述的设备,其中,在执行所述Java程序的处理器是多核处理器的情况下,所述用于在对应于所述Java程序的Java进程中创建辅助线程,并将所述辅助线程挂接到在该Java进程中创建的Java虚拟机JVM的装置创建多个所述辅助线程。
12.根据权利要求11所述的设备,其中,创建的多个所述辅助线程的数目与多核处理器的核数目相同。
13.根据权利要求12所述的设备,其中,创建的多个所述辅助线程中的每一个被分别绑定到多核处理器的一个核。
14.根据权利要求10所述的设备,还包括用于响应于每个Java线程的启动,通过回调函数建立该Java线程和操作系统内核中的对应于该Java线程的本地任务之间的映射关系的装置。
15.根据权利要求14所述的设备,其中,所述信号包含被阻塞的本地任务的ID,并且其中,所述用于在所述辅助线程响应于接收到来自操作系统内核的所述信号从所述JVM中取回调用栈信息,并利用所取回的调用栈信息定位到所述Java程序的源代码中的引起所述阻塞的位置的装置包括用于根据本地任务的ID和所述映射关系,从所述JVM中取回与该本地任务对应的Java线程的调用栈信息的装置。
16.根据权利要求10所述的设备,其中,所述探测器被插入在操作系统的调度器中并且在发生任务上下文切换的情况下执行操作。
17.根据权利要求16所述的设备,其中,所述探测器是被加载到操作系统内核中的用户自定义的模块插入到所述调度器中的。
18.根据权利要求16或17所述的设备,其中,所述用于由所述探测器监视所述Java进程中的Java线程在操作系统内核中的状态并且响应于检测到Java线程被阻塞而向所述辅助线程发送信号的装置包括用于在处理器执行任务上下文切换的情况下从处理器调出的本地任务对应于所述Java进程中的Java线程并且该本地任务处于阻塞状态的情况下向所述辅助线程发送信号的装置。
全文摘要
本发明涉及用于定位Java程序的瓶颈的方法和设备。提供了一种用于定位Java程序的瓶颈的方法,包括以下步骤在对应于所述Java程序的Java进程中创建辅助线程,并将所述辅助线程挂接到在该Java进程中创建的Java虚拟机;在操作系统内核中插入探测器;所述探测器监视所述Java进程中的Java线程在操作系统内核中的状态并且响应于检测到Java线程被阻塞而向所述辅助线程发送信号;以及所述辅助线程响应于接收到来自操作系统内核的所述信号,从所述JVM中取回调用栈信息,并利用所取回的调用栈信息定位到所述Java程序的源代码中的引起所述阻塞位置。
文档编号G06F11/36GK102222037SQ201010150110
公开日2011年10月19日 申请日期2010年4月15日 优先权日2010年4月15日
发明者李影, 滕启明, 王海川, 钟虓 申请人:国际商业机器公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1