在本教程中,我们创建了一个简单的Java程序,使您可以逐步学习该技术。

故障排除工具

本教程将在下面使用以下工具:

  • (查明高CPU线程贡献者)
  • (用于代码级别的线程关联和根本原因分析)

高CPU模拟器Java程序

下面的简单程序只是循环并创建新的String对象。 这将使我们能够按线程分析执行此CPU。 我建议您将其导入您选择的IDE中,例如Eclipse并从那里运行它。 执行Windows计算机后,应立即观察到CPU数量的增加。

package org.ph.javaee.tool.cpu;

/**
 * HighCPUSimulator
 * @author Pierre-Hugues Charbonneau
 * http://javaeesupportpatterns.blogspot.com
 *
 */
public class HighCPUSimulator {
      
       private final static int NB_ITERATIONS = 500000000;
      
       // ~1 KB data footprint
       private final static String DATA_PREFIX = "datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatad
atadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
datadatadatadatadatadatadata";

      
       /**
        * @param args
        */
       public static void main(String[] args) {
            
             System.out.println("HIGH CPU Simulator 1.0");
             System.out.println("Author: Pierre-Hugues Charbonneau");
             System.out.println("http://javaeesupportpatterns.blogspot.com/");

             try {


                    for (int i = 0; i < NB_ITERATIONS; i++) {
                          
                           // Perform some String manipulations to slowdown and expose looping process...
                           String data = DATA_PREFIX + i;                
                          
                    }

             } catch (Throwable any) {
                    System.out.println("Unexpected Exception! " + any.getMessage()
                                        + " [" + any + "]");
                   
             }

             System.out.println("HighCPUSimulator done!");
       }

}

步骤#1 –启动流程浏览器

Process Explorer工具以可视方式动态显示CPU使用情况。 这对于实时分析非常有用。 如果您需要每个线程在CPU上的历史数据,则还可以将Windows perfmon与%Processor Time&Thread Id数据计数器一起使用。 您可以从下面的链接下载Process Explorer:
http://technet.microsoft.com/zh-cn/sysinternals/bb896653

在我们的示例中,您可以看到在执行示例程序之后,Eclipse javaw.exe进程现在使用了约25%的CPU利用率。





步骤#2 –启动Process Explorer的Threads视图

下一步是显示javaw.exe进程的“线程”视图。 只需右键单击javaw.exe进程,然后选择“属性”。 将根据以下快照打开“线程”视图:





  • 第一列是线程ID(十进制格式)
  • 第二列是每个线程使用的CPU利用率%–
  • 第三列也是另一个计数器,指示线程是否正在CPU上运行

在我们的示例中,我们可以看到罪魁祸首是使用约25%CPU的线程ID#5996。

步骤#3 –生成JVM线程转储

此时,Process Explorer将不再有用。 目的是查明一个或多个Java线程,这些线程消耗了我们实现的大部分Java进程CPU利用率。 为了进入分析的下一个层次,您将需要捕获JVM线程转储。 这将使您能够将线程ID与线程堆栈跟踪相关联,从而可以查明这种处理类型正在消耗如此多的CPU。
JVM线程转储的生成可以通过几种方式完成。 如果您使用的是JRockit VM,则可以按照以下示例使用jrcmd工具:





获得线程转储数据后,只需搜索线程ID并找到您感兴趣的线程堆栈跟踪。
在我们的示例中,从Eclipse触发的线程“主线程”被暴露为主要罪魁祸首,而这正是我们想要演示的。

Main Thread id=1 idx=0x4 tid=5996 prio=5 alive, native_blocked
  at org/ph/javaee/tool/cpu/HighCPUSimulator.main
   (HighCPUSimulator.java:31)
  at jrockit/vm/RNI.c2java(IIIII)V(Native Method)
  -- end of trace

步骤#4 –分析罪魁祸首的线程堆栈跟踪并确定根本原因

此时,您应该具备进行根本原因分析所需的一切。 您将需要检查每个线程堆栈跟踪,并确定要处理的问题类型。 最后一步通常是您将花费大部分时间的地方,问题可能很简单(例如无限循环),也可能很复杂(例如与垃圾回收相关的问题)。

在我们的示例中,线程转储确实揭示了CPU的高CPU数量来自第31行附近的示例Java程序。正如所料,它确实揭示了我们为本教程专门设计的循环条件。

for (int i = 0; i < NB_ITERATIONS; i++) {
      // Perform some String manipulations to slowdown and expose looping process...
      String data = DATA_PREFIX + i;                
 }

我希望本教程可以帮助您了解如何分析和帮助查明Windows OS上Java CPU问题的根本原因。 请继续关注更多更新,下一篇文章将为您提供Java CPU故障排除指南,包括如何处理最后的分析步骤以及常见问题模式。

参考: Java EE支持模式和Java教程博客中的JCG合作伙伴 Pierre-Hugues Charbonneau在Windows上的Java线程CPU分析