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 显示了一个代码片段示例:
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 |
要使用 Java 5 SR9 及其早期版本或者 Java 6 SR4 及其早期版本并通过代理来启动应用程序,请使用:
java -agentlib:healthcenter -Xtrace:output=healthcenter.out GenerateDeadlock |
您可以下载 IBM 开发人员工具包。
编写增强的监视应用程序
要修改您在 第 1 部分中创建的 DeadlockDemo 应用程序,请打开 Application.java 文件(参见图 2):
将 Application.java 中的所有代码替换为源代码(参见 下载)。清单 2 显示了一个代码片段示例:
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):
显然,您已经拥有了一些数据。现在让我们来理解一下它们的含义。
分析数据的含义
在顶部面板中,样例计数会随着应用程序的运行而增加,显示该方法占用了大多数 CPU 周期。清单 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):
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 所示:
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):
现在可以再次启动该过程(确定热门方法并帮助解决性能瓶颈)。
结束语
这些文章介绍了如何开始使用 Health Center API 的基础知识。您可以应用这些技术来提取和使用 Health Center 中的任何数据。例如,仅当 WebSphere®方法变为热门方法或者似乎因为应用程序内存不足而触发系统转储时,可以使用此 API 获取这些方法的详细信息。