通过 Health Center API 监视 Java 应用程序,第 2 部分

向死锁检测应用程序添加方法分析视图

Health Center 2.1 版包含一个功能强大的 API。该 API 允许 Java 开发人员将 Health Center 嵌入到其应用程序中,还允许利用其监视功能来解决问题。本系列的第 2 部分将采用 第 1 部分中的死锁检测应用程序,并添加一个方法分析视图来显示应用程序的哪些地方花费了大部分的 CPU 周期。

Toby Corbin, 软件工程师, IBM

Photo of Toby CorbinToby Corbin 是 IBM Java 技术中心的一名软件工程师,目前正在开发 RAS 工具。他于 2001 加入 IBM,四年的时间里一直在开发 Java Runtime Environment 的国际语言支持和全球化,之后的两年从事 Swing 和 AWT 库的开发。



2013 年 6 月 06 日

Health Center 是一款适用于 Java 的 IBM®监视和诊断工具,是一个免费的低开销诊断工具和 API,用于监视在 IBM Java 虚拟机 (JVM) 上运行的应用程序。有关此 API 可以执行的操作的细节,请参阅 第 1 部分。在本文中,将会采用 第 1 部分中开发的死锁检测应用程序,并添加一个方法分析视图来显示应用程序的哪些地方花费了大部分的 CPU 周期。(请参阅 下载,以便获得示例的完整源代码。)

系统要求

Health Center API bundle 至少需要安装 Eclipse 3.4 或 Eclipse 4.x。

测试应用程序

第 1 部分中,已经测试了生成死锁条件的应用程序。在本文中,我们将使用该应用程序的修订版,该版本包含一些会给 CPU 施加压力的功能。请参阅 下载,以获得相关的源代码。清单 1 显示了一个代码片段示例:

清单 1. 新的 GenerateDeadLock 源代码的代码片段
 private class runSlowMethods extends Thread { 

   public void run() { 
      SlowClassAndMethod1 sCAM1 = new SlowClassAndMethod1(); 
      SlowClassAndMethod2 sCAM2 = new SlowClassAndMethod2(); 

      sCAM1.start(); 
      sCAM2.start(); 
   } 

   private class SlowClassAndMethod1 extends Thread { 

      public void run() { 
         while (true) { 
            slowMethod1(); 
            try { 
               Thread.sleep(2000); 
            } catch (InterruptedException e) { 
               e.printStackTrace(); 
            } 
         } 
      } 

      private void slowMethod1() { 
         String largeString = new String("a string to add"); 
         for (int i = 0; i < 1000000; i++) { 
            largeString.concat(largeString); 
         } 

      } 
   } 

   private class SlowClassAndMethod2 extends Thread { 

      public void run() { 
         while (true) { 
            slowMethod2(); 
            try { 
               Thread.sleep(2000); 
            } catch (InterruptedException e) { 
               e.printStackTrace(); 
            } 
         } 
      } 

      private void slowMethod2() { 
         HashMap map = new HashMap(); 
         String largeString = new String("another string to add"); 
         for (int i = 0; i < 1000000; i++) { 
            map.put(i, largeString); 
            largeString.concat(largeString); 
         } 

      }

通过与该程序连接的 Health Center 代理启动该程序。要使用 Java 5 SR10 及更高版本、Java 6 SR5 及更高版本和 Java 7 并通过代理来启动该应用程序,请使用以下命令(参见图 1):

 java -Xhealthcenter GenerateDeadlock
图 1. 启动应用程序
要安装的命令行软件的屏幕截图

要使用 Java 5 SR9 及其早期版本或者 Java 6 SR4 及其早期版本并通过代理来启动应用程序,请使用:

 java -agentlib:healthcenter -Xtrace:output=healthcenter.out GenerateDeadlock

您可以下载 IBM 开发人员工具包

编写增强的监视应用程序

要修改您在 第 1 部分中创建的 DeadlockDemo 应用程序,请打开 Application.java 文件(参见图 2):

图 2. 打开 Application.java
插件 application.java 的屏幕截图

将 Application.java 中的所有代码替换为源代码(参见 下载)。清单 2 显示了一个代码片段示例:

清单 2. Application.java 替换代码的代码片段
 private class ProfileApplication extends Thread { 

   String methodTree = null; 

   public void run() { 

      try { 
         Thread.sleep(10000); 
      } catch (InterruptedException e) { 
         e.printStackTrace(); 
      } 

      while (keepRunning) { 
         analyzeMethods(); 
         try { 
            Thread.sleep(2000); 
         } catch (InterruptedException e) { 
            e.printStackTrace(); 
         } 
      } 
   } 

   private void analyzeMethods() { 
      ProfilingData profilingData; 
      profilingData = hcMon.getProfilingData(); 

      if (profilingData == null) { 
         return; 
      } 

      MethodProfileData[] mPD = profilingData.getProfilingEvents(); 
      if (mPD != null) { 

         final SimpleProfileData[] sPD = new SimpleProfileData[mPD.length]; 
         int index = 0; 
         for (MethodProfileData mP : mPD) { 
            methodTree = new String(); 
            sPD[index] = new SimpleProfileData(); 
            sPD[index].setCount(mP.getMethodSampleCount()); 
            sPD[index].setMethodName(mP.getMethodName()); 
            MethodProfilingNode[] mPN = mP.getCallingMethods(); 
            for (MethodProfilingNode node : mPN) { 
               if (node instanceof MethodProfilingNode) { 
                  walkProfileTree(node); 
               } 
            } 
            sPD[index].setMethodTree(methodTree); 
            index++; 
         } 

         Arrays.sort(sPD); 

         display.asyncExec(new Runnable() { 
            public void run() { 

               profileText.setText("method: "
                     + sPD[0].getMethodName() 
                     + "\n 		 sample count:  "
                     + sPD[0].getCount() + "\n"
                     + sPD[0].getMethodName() 
                     + sPD[0].getMethodTree()); 
               shell.redraw(); 
            } 
         }); 

      } 
   }

该代码不同于原始代码,但大部分更改都是为了设置应用程序的显示和应用正确的应用程序行为。实际的 Health Center API 代码仅包含对方法分析数据的更多调用,您会在下一节中看到这些调用。


对数据进行分析

Health Center 使用了一个基于样例的分析器,该分析器为您提供了一个应用程序所使用的所有方法的样例视图。该分析器对于确定性能瓶颈非常有用,因为您的应用程序可能在某个方法中花费了太多的时间,从而导致性能问题。

要访问分析数据,请使用一个与访问线程数据的调用类似的调用:

 ProfilingData profilingData = HealthCenter.getProfilingData();

ProfilingData类包含几个方法,这些方法提供了对所有抽样方法、样例计数和调用层次结构的访问。

方法 getProfilingEvents()返回 MethodProfileData的一个数组,其中包含每个抽样方法的条目。对于每个抽样方法,可以使用 getMethodSampleCount()获取样例计数,使用 getMethodName()获取方法名称,并使用 getCallingMethods() 调用它的方法。

使用这些方法可生成大多数样例方法的调用层次结构。然后可以使用该信息来提高所监视应用程序的性能。


监视死锁和方法分析

现在,您已经拥有了新的死锁和分析监视应用程序,并且改进了受感染死锁的程序正在后台运行。运行此死锁和分析监视应用程序可以查看应用程序监视窗口上的 “已检测到的死锁” 消息(参见图 3):

图 3. 监视死锁
应用程序正在监视的死锁的屏幕截图

显然,您已经拥有了一些数据。现在让我们来理解一下它们的含义。


分析数据的含义

在顶部面板中,样例计数会随着应用程序的运行而增加,显示该方法占用了大多数 CPU 周期。清单 3 仅显示了顶部方法的输出:

清单 3. 输出顶部方法
 profileText.setText("method: "
   + sPD[0].getMethodName() 
   + "\n 		 sample count:  "
   + sPD[0].getCount() + "\n"
   + sPD[0].getMethodName() 
   + sPD[0].getMethodTree());

但是,您可以通过循环 sPD 数组中的所有元素来输出更多的方法。

当在 Health Center 中运行分析器时,如果某个方法正在堆栈上运行 Java 代码,那么该方法将会获得计数。方法拥有的样例越多,它所执行的工作也就越多。样例计数可能无法反映调用方法的频率。但它可以表明相对于应用程序中的其他方法该方法指定的工作的多少。

在这个示例中,您可以看到方法 java.util.HashMap.rehash(int)是抽样最多的方法。它的调用堆栈(调用它的起始位置)显示它源自 GenerateDeadlock$runSlowMethods$SlowClassAndMethod2.run()中的某个调用。

查看层次结构中的下一个调用,run()方法调用了 GenerateDeadlock$runSlowMethods$SlowClassAndMethod2.slowMethod2(),但它还负责调用 java.util.HashMap.put。这会最终导致调用 rehash()

如果查看死锁应用程序中的 slowMethod2()方法,就会看到一个循环,该循环将条目置于 HashMap 中(参见清单 4):

清单 4. 将条目置于 HashMap 中的循环
 private void slowMethod2() { 
   HashMap map = new HashMap(); 
   String largeString = new String("another string to add"); 
   for (int i = 0; i < 1000000; i++) { 
      map.put(i, largeString); 
      largeString.concat(largeString); 
   } 
 }

您可以在该方法中注释掉该循环,如清单 5 所示:

清单 5. 注释掉循环
   private void slowMethod2() { 
      HashMap map = new HashMap(); 
      String largeString = new String("another string to add"); 
 //      for (int i = 0; i < 1000000; i++) { 
 //         map.put(i, largeString); 
 //         largeString.concat(largeString); 
 //      } 
   }

如果再次运行测试应用程序,然后运行新的死锁和分析工具,您会注意到该问题已得到解决。一个新的方法出现在分析面板中(参见图 4):

图 4. 问题已解决 — 出现了新的方法
分析面板中的新方法的屏幕截图

现在可以再次启动该过程(确定热门方法并帮助解决性能瓶颈)。


结束语

这些文章介绍了如何开始使用 Health Center API 的基础知识。您可以应用这些技术来提取和使用 Health Center 中的任何数据。例如,仅当 WebSphere®方法变为热门方法或者似乎因为应用程序内存不足而触发系统转储时,可以使用此 API 获取这些方法的详细信息。


下载

描述名字大小
文章的源代码j-healthcareapi2-source.zip3KB

参考资料

学习

获得产品和技术

讨论

  • Health Center 论坛:参与与 Health Center 相关的讨论。
  • 加入 developerWorks 社区:探索由开发人员驱动的博客、论坛、组和维基,并与其他 developerWorks 用户进行交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=932816
ArticleTitle=通过 Health Center API 监视 Java 应用程序,第 2 部分
publish-date=06062013