使用 Java Debug Interface 调试多线程应用程序(不太懂)

 注册事件

  分析器和目标程序之间采用基于事件的模式进行通信。分析器向虚拟机实例注册所关注的事件。事件发生时,虚拟机将相关事件信息放入事件队列中,采用 生产者 - 消费者 的模式与分析器同步。

  注册事件

  EventRequestManager 管理事件请求,它支持创建、删除和查询事件请求。EventRequest 支持三种挂起策略:

  EventRequest.SUSPEND_ALL : 事件发生时,挂起所有线程

  EventRequest.SUSPEND_EVENT_THREAD : 事件发生时,挂起事件源线程

  EventRequest.SUSPEND_NONE : 事件发生时,不挂起任何线程

  JDI 支持多种类型的 EventRequest,如 ExceptionRequest,MethodEntryRequest,MethodExitRequest,ThreadStartRequest 等,可以参考 文档。

  清单 4. 注册事件


 EventRequestManager mgr = vm.eventRequestManager();
  // 注册异常事件
  ExceptionRequest excReq = mgr.createExceptionRequest(null, true, true);
  excReq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
  excReq.enable();
  // 注册进方法事件
  MethodEntryRequest menr = mgr.createMethodEntryRequest();
  menr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
  menr.enable();
  // 注册出方法事件
  MethodExitRequest mexr = mgr.createMethodExitRequest();
  mexr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
  mexr.enable();
  // 注册线程启动事件
  ThreadStartRequest tsr = mgr.createThreadStartRequest();
  tsr.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
  tsr.enable();
  // 注册线程结束事件
  ThreadDeathRequest tdr = mgr.createThreadDeathRequest();
  tdr.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
  tdr.enable();

  分析器从事件队列中获取事件

  EventQueue 用来管理目标虚拟机实例的事件,事件会被加入 EventQueue 中。分析器调用 EventQueue.remove(),如果事件队列中存在事件,则返回不可修改的 EventSet 实例,否则分析器会被挂起直到有新的事件发生。处理完 EventSet 中的事件后,调用其 resume() 方法唤醒 EventSet 中所有事件发生时可能挂起的线程。

  清单 5. 获取事件


public void run() {
  EventQueue queue = vm.eventQueue();
  while (connected) {
  try {
  EventSet eventSet = queue.remove();
  EventIterator it = eventSet.eventIterator();
  while (it.hasNext()) {
  handleEvent(it.nextEvent());
  }
  eventSet.resume();
  } catch (InterruptedException exc) {// Ignore
  } catch (VMDisconnectedException discExc) {
  handleDisconnectedException();
  break;
  }
  }
  }

  获取多线程执行信息

  执行流程和变量信息是调试程序最重要的两方面。无论是通过 IDE 设置断点的调试方式,还是通过在程序中记 Log 的调试方式,它们的主要目的是向开发者提供以上两方面信息。本文分析器以单个线程为单位,来记录线程运行信息:

  帧 (StackFrame) 详情

  关于帧的详细介绍,可以参见 这里。

  执行流程。分析器以方法作为最小颗粒度单位。分析器按照实际的线程执行顺序记录方法进出。

  变量值。对于单个方法而言,其程序逻辑固定,方法的输入值决定了方法内部执行流程。分析器将在方法入口和出口分别记录该方法作用域内可见变量,便于开发者调试。

  执行栈信息记录。当异常发生时,执行栈中完好地保存了调用帧信息。分析器获取线程栈中的所有帧,并记录每个帧记录的信息,其中包含可见变量值、帧调用名称等信息。StackFrame 中变量信息的获取也是 JDI 所提供的特殊能力之一。

  与 IDE 设置断点的方法相比,提供的数据信息量相当,但分析器提供执行流程信息更加的清晰;与在程序中记录 Log 的方式相比,分析器在执行流程和信息量两方面都胜出。

  以下将详细介绍上面三方面信息抓取:

  线程执行流程

  线程执行流程可划分:线程启动→ run() →进入方法→ ... →退出方法→线程结束。通过向虚拟机实例注册 ThreadStartRequest,MethodEntryRequest,MethodExitRequest 和 ThreadDeathRequest 事件的方式记录执行过程。事件注册详细见清单 4,清单 6 列出分析器对于以上事件的处理方法。

  清单 6. 获取执行流程


 void threadStartEvent(ThreadStartEvent event) {
  println("Thread " + event.thread().name() + " Start");
  }
  void methodEntryEvent(MethodEntryEvent event) {
  println("Enter Method:" + event.method().name() + " -- "
  + event.method().declaringType().name());
  // 进入方法记录可见变量值
  this.printVisiableVariables();
  }
  void methodExitEvent(MethodExitEvent event) {
  println("Exit Method:" + event.method().name() + " -- "
  + event.method().declaringType().name());
  // 退出方法记录可见变量值
  this.printVisiableVariables();
  }
  void threadDeathEvent(ThreadDeathEvent event) {
  println("Thread " + event.thread().name() + " Dead");
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值