如何用TomProbe轻松定位CPU异常的代码?

 

我的网站运行一段时间,Tomcat的CPU突然变成99%,并且以后一直是高CPU,与宕机几乎无异;我的Tomcat平时CPU是0%,有时会出现20%的CPU,我想知道是哪段代码引起的,要优化;......。问题随机出现,无法复现,查找起来真是花时间、真是令人抓狂!

 

TomProbe可以很容易地定位引起CPU异常的代码,利用Thread.currentThread().setName()方法巧妙地设置代码标记!直接上例子。BigWork.java:

 

import java.io.IOException;

import java.io.PrintWriter;

 

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

public class BigWork extends HttpServlet{

 

  @Override

  public void doGet(HttpServletRequest request, HttpServletResponse response) 

              throws IOException, ServletException{

    System.out.println("BigWork doGet!");

 

    String oldName=Thread.currentThread().getName();

    Throwable ex0=null;

 

    System.out.println("oldName="+oldName);

    System.out.println("Thread.currentThread()="+Thread.currentThread());

    System.out.println("Long.MAX_VALUE="+Long.MAX_VALUE);

 

    try{

      long i=0;

      long start=System.currentTimeMillis();

      Thread.currentThread().setName("BigWork's doGet, i="+i);

      while (i<Long.MAX_VALUE){

        i++;

        if (i%1000000000==0){

          Thread.currentThread().setName("BigWork's doGet, sleep!");

          try{

            Thread.sleep(1);

          }

          catch (Throwable ex2){

          }

          Thread.currentThread().setName("BigWork's doGet, i="+i);

        }

      }

      Thread.currentThread().setName("BigWork's doGet, after while(...)");

      long end=System.currentTimeMillis();

      long cost=end-start;

 

      System.out.println("cost="+cost);

 

      response.setContentType("text/html");

      response.setCharacterEncoding("UTF-8");

      PrintWriter out=response.getWriter();

      out.println("<html>");

      out.println("<head>");

      out.println("<title>BigWork</title>");

      out.println("</head>");

      out.println("<body bgcolor=\"white\">");

      out.println("<h1>cost="+(cost/1000)+"s</h1>");

      out.println("</body>");

      out.println("</html>");

    }

    catch (Throwable ex1){

      ex0=ex1;

      ex1.printStackTrace();

    }

    Thread.currentThread().setName(oldName);

 

    System.out.println("after setName="+oldName);

    System.out.println("Thread.currentThread()="+Thread.currentThread());

 

    if (ex0!=null){

      if (ex0 instanceof ServletException){

        throw (ServletException)ex0;

      }

      if (ex0 instanceof IOException){

        throw (IOException)ex0;

      }

    }

  }

}

(编译、配置、启动Tomcat等省去)启动Tomcat、在浏览器请求BigWork服务,这时Tomcat的窗口显示BigWork的doGet()运行了。

 

   

 

(下图)启动TomProbe、连接到Tomcat,界面显示CPU占用厉害,异常!

 

   

 

(下图)将光标点在高CPU点上,右击鼠标,弹出菜单。

     

 

(下图)点“查看线程信息(最新CPU前1-6)”菜单后,显示了前6名CPU线程信息,正是BigWork的doGet()的while (true)循环消耗了大量CPU!

   

 

 Q1: 好象很不错,但我有点疑惑:上面BigWork.java里2处Thread.currentThread()是同一个线程吗?若不是,岂不是搞乱了。

 

答:你做的servlet、jsp等网站开发,其实是在填写代码片段。Tomcat等Web Server是这样运行代码的:当有用户请求时,从线程池取出一个空闲线程,执行run()方法,run()方法是这样的:

public void run(){

  objectA.method2();

  ........

  objectX.methodn();    

}  

你的servlet、jsp的doGet()、doPost()、service()等方法必作为一个整体被同一个线程执行,因此一个方法内的多处Thread.currentThread()都是同一个线程。

 

 

  Q2:我是项目技术负责人,按您的说法,我让项目组每个程序员在自己的代码里加上类似的代码标记,那我就可以利用TomProbe很轻松地发现哪些代码需要优化、哪些程序员代码质量有问题,那岂不是很省事、岂不是太妙了?

 

答:你的悟性真高!