基于JVMTI实现Java线程的监控 - 编程入门网
线程的监控(3)
时间:2011-02-06 IBM 李凌
获取显式线程切换的上下文信息 在 Java 方法调用发生的时候,通过 JVMTI 接口能够很轻易的获取到方法名称和执行该方法的线程标识。因此,确定线程切换上下文三元组 < 操作线程,动作,被操作线程 > 的关键在于,如何获取被操作线程对象标识,而这需要对 Java 方法的调用机制进行分析。 每当启动一个新线程的时候,Java 虚拟机会为它分配一个 Java 栈。Java 栈以帧(Frame)为单位保存线程的运行状态。虚拟机只会对 Java 栈执行两种操作:以帧为单位的入栈和出栈。 当线程调用一个 Java 方法时,虚拟机会在该线程所在的 Java 栈压入一个新的栈帧(Stack Frame),用于存储该 Java 方法的地址;该方法被称为当前方法,该栈帧被成为当前栈帧。栈帧通常由局部变量区、操作数栈和帧数据区组成。在执行当前方法时,它使用当前栈帧来存储参数、局部变量、中间运算结果等等数据。栈帧在方法调用的时候被创建,并在方法完成的时候销毁。 通过对栈帧的进一步研究发现,当一个对象的某个实例方法执行时,Java 虚拟机会隐式地在该方法的当前栈帧的局部变量区加入一个指向该对象的引用。尽管在方法源代码中并没有显式声明这个参数,但这个参数对于任何一个实例方法都是由 JVM 隐含加入的,而且它始终在局部变量区的首位。局部变量区是根据索引进行寻址的,第一个局部变量的索引是 0,因此可以使用局部变量区索引 0 的方式来访问这个对象引用,如表 1 所示。 表 1. 局部变量区
如此一来,当被操作线程对象执行某个线程方法的时候,可以通过分析当前操作线程的当前栈帧的本地方法区获取到被操作线程对象的引用。这样,就可以完整地确定显式线程切换的三元组 < 操作线程,动作,被操作线程 > 信息。 需要注意的是,有一些线程方法,例如 sleep() 和 yield() 方法,它们是作为本地方法(Native Method)来实现的,它们在被调用的过程中不会生成 Java 栈中的当前方法帧,而是将信息保存在本地方法栈(Native Stack)内。因此,对这些方法引起的线程切换不能直接采用上述分析方法,而是应该分析本地方法栈。 获取隐式线程切换的上下文信息 类似的,获取隐式线程切换上下文信息的关键也是确定三元组 < 操作线程,动作,被操作线程 > 中的被操作线程对象标识,而这需要对 Java 线程的同步机制进行分析。 Java 使用名为监视器(Monitor)的同步机制来调节多个线程的活动和共享数据,如图 4 所示。Java 中,每个对象都对应一个监视器,即使在多线程的情况下,该监视器监视的区域同一时间只会被一个线程执行。一个线程想要执行监视区域的代码,唯一的途径就是获得该区域对应的监视器。当一个线程到达了一个监视区域的开始处(Entry Point),它就会被置入该监视器的请求集(Entry Pool)。如果此时没有其他线程在请求集中等待,并且也没有其它线程正持有该监视器(Monitor Hold),那么该线程就可以获得监视器,并继续执行监视区域中的代码。当这个线程执行完监视区域代码后,它就会退出并释放该监视器(Release Monitor)。如果线程在执行监视区域代码的过程中,执行了 wait() 方法,那么该线程会暂时释放该监视器,并被置入等待集(Wait Pool),等待被唤醒;如果线程在这个过程中执行了 notify() 方法,那么在等待集中的某个线程就会被标记为即将 |
||||||||||
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |