利用性能监控日志

  因为性能的问题不是很容易重现,当product环境中遇到性能问题的时候,如果是数据的问题,也许当你把product 数据copy到你的测试环境中,能重现比较慢点查询,加以改进。但是如果是并发用户或者网络等运行时环境的问题,你很难重现。这时,如果你能通过日志看到那些关键的响应慢的方法,也许可以帮助你快点找到问题所在。下面的代码可以帮你做到这一点,仅供参考:

import org.slf4j.Logger;  
    
  public class TraceUtil {  
      final Logger logger;  
      final long threshold = 1000;  
      private long begin;  
      private long offtime = 0;  
      private String threadInfo;  
      private String targetId;  
    
      public TraceUtil(Logger logger, Thread thread, String targetId, long begin) {  
          this.logger = logger;  
          this.threadInfo = thread.getId() + "-" + thread.toString();  
          this.targetId = targetId;  
          this.begin = begin;  
      }  
    
      public void trace(String targetEvent) {  
          long duration = System.currentTimeMillis() - begin;  
          long increment = duration - offtime;  
          offtime = duration;  
          float percentage = (float) increment / (float) duration * 100;  
          if (duration > threshold && percentage > 20) {  
              logger.error(  
                      "Response time is too large: [{}], {}/{} ({}), {}, {}",  
                      new String[] { threadInfo + "", increment + "",  
                              duration + "", percentage + "%", targetEvent,  
                              targetId });  
          }  
    
      }  
    
  }
   利用JVM的MXBean找到blocked的点

  当你发现JVM占用的cpu很高,而且响应时间比较慢,很可能是被IO或者网络等慢速设备拖住了。也有可能是你的方法中某个同步点(同步方法或者对象)成为性能的瓶颈。这时候你可以利用JVM提供的monitor API来监控:

<%@ page import="java.lang.management.*, java.util.*" %>   
    <%!   
        Map cpuTimes = new HashMap();   
        Map cpuTimeFetch = new HashMap();   
    %>   
       
    <%   
    out.println("Threads Monitoring");   
    long cpus = Runtime.getRuntime().availableProcessors();   
    ThreadMXBean threads = ManagementFactory.getThreadMXBean();   
    threads.setThreadContentionMonitoringEnabled(true);   
    long now = System.currentTimeMillis();   
    ThreadInfo[] t = threads.dumpAllThreads(false, false);   
    for (int i = 0; i < t.length; i++) {   
        long id = t[i].getThreadId();   
        Long idObj = new Long(id);   
        long current = 0;   
        if (cpuTimes.get(idObj) != null) {   
            long prev = ((Long) cpuTimes.get(idObj)).longValue();   
            current = threads.getThreadCpuTime(t[i].getThreadId());   
            long catchTime = ((Long) cpuTimeFetch.get(idObj)).longValue();   
            double percent = (double)(current - prev) / (double)((now - catchTime) * cpus * 1000);   
            if (percent > 0 && prev > 0) {  
    out.println("<li>" + t[i].getThreadName()+"#"+t[i].getThreadId() + " Time: " + percent + " (" + prev + ", " + current + ") ");  
    String locked = t[i].getLockInfo()==null?"":t[i].getLockInfo().getClassName();  
    out.println(" Blocked: (" + t[i].getBlockedTime() + ", " + t[i].getBlockedCount() + ", " + locked + ")</li>");  
    }  
        }   
        cpuTimes.put(idObj, new Long(current));     
        cpuTimeFetch.put(idObj, new Long(now));   
    }   
    %>
 
  同步是性能的一大瓶颈

  通过监控发现,大量线程block在一个同步方法上,这样cpu也使不上劲。当你发现性能上不去,IO和网络等慢速设备也不是问题的时候,你得检查一下是否在某个关键点上使用了同步(synchronizae)。有时候也许是你应用的第三方的jar里面的某个方法是同步的,这种情况下,你很难找到问题所在。只能在编写代码的时候看一下你引用的方法是否是同步的。