一次java性能调优总结

   我们的系统中新开发了一个数据抽取的功能,东西做完后,一看执行时间那叫一个恼火。参考同类系统同样功能的执行时间,目标:将本地数据处理时间压缩到5秒以内。

   

  第一步:

   要想知道哪个地方需要优化,仅凭感觉还是不够,我使用btrace寻找速度慢点原因。下面贴出这次使用的btrace代码:

import static com.sun.btrace.BTraceUtils.name;  
import static com.sun.btrace.BTraceUtils.print;  
import static com.sun.btrace.BTraceUtils.println;  
import static com.sun.btrace.BTraceUtils.probeClass;  
import static com.sun.btrace.BTraceUtils.probeMethod;  
import static com.sun.btrace.BTraceUtils.str;  
import static com.sun.btrace.BTraceUtils.strcat;  
import static com.sun.btrace.BTraceUtils.timeMillis;  
  
import com.sun.btrace.annotations.BTrace;  
import com.sun.btrace.annotations.Kind;  
import com.sun.btrace.annotations.Location;  
import com.sun.btrace.annotations.OnMethod;  
import com.sun.btrace.annotations.TLS;  
  
/** 
 * 监控方法耗时 
 *  
  */  
@BTrace  
public class PrintTimes {  
  
    /** 
     * 开始时间 
     */  
    @TLS  
    private static long startTime = 0;  
  
    /** 
     * 方法开始时调用 
     */  
    @OnMethod(clazz = "com.xxx.service.XXService", method = "/.+/")  
    public static void startMethod() {  
        startTime = timeMillis();  
    }  
  
    /** 
     * 方法结束时调用<br> 
     */  
    @SuppressWarnings("deprecation")  
    @OnMethod(clazz = "com.xxx.service.XXService", method = "/.+/", location = @Location(Kind.RETURN))  
    public static void endMethod() {  
  
        print(strcat(strcat(name(probeClass()), "."), probeMethod()));  
        print("  [");  
        print(strcat("Time taken : ", str(timeMillis() - startTime)));  
        println("]");  
    }  
}

本段代码会匹配 com.xxx.service.XXService 类中的所有方法,打印每个方法的执行时间,每调用一次会打印一次,不是总时间。更多BTrace内容请访问官网:https://kenai.com/projects/btrace

第二步到了这一步已经知道了速度慢集中在哪些点。对于这个系统我可以做哪些工作来优化呢?

1.代码方面调整
   a.并行化  (关于线程池线程数量的调整,请参考我的另外一篇关于"设置最优线程数"的文章)
   b.增加缓存
c.插入操作执行频繁的地方,用原生sql进行批量插入(持久化层使用EclipseLink)
d.避免大数据量表join操作
2.参数调整 a.JVM内存参数 b.数据库连接池参数

3.功能实现设计调整 (最不愿看到的)

根据实际情况采取相应手段调整。

第三步

  检测优化后系统该功能性能情况,如果有效则保留,否则恢复代码到本次优化之前的那个状态。

 

第三步结束后,继续执行第一、二、三步。直到性能达到预期要求。

根据对待优化代码进行分析,最后采用了并行化,增加缓存以及批量插入优化的操作。软件参数和功能设计方面不需要调整。

 

--------------- 问题纪录 -------------

在将插入操作改成批量插入时,日志报以下错误:

Caused by: org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
14:45:03.821 [pool-1-thread-2] ERROR java.lang.Throwable -     at org.apache.tomcat.dbcp.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:114)
14:45:03.822 [pool-1-thread-2] ERROR java.lang.Throwable -     at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
14:45:03.822 [pool-1-thread-2] ERROR java.lang.Throwable -     at org.eclipse.persistence.sessions.JNDIConnector.connect(JNDIConnector.java:123)
14:45:03.822 [pool-1-thread-2] ERROR java.lang.Throwable -     ... 39 more
14:45:03.823 [pool-1-thread-2] ERROR java.lang.Throwable - Caused by: java.util.NoSuchElementException: Timeout waiting for idle object

有几个思路:
1.数据库连接没有成功释放;(得到数据库连接池的监控数据,可以实时看到空闲连接的情况)
2.数据库连接成功释放,但是由于多个线程同时抢占连接而导致有些线程抢不到连接。(这个情况考虑增大数据库连接池) 

使用BTrace监控连接池发现只有获得连接没有释放连接操作,看来属于1。由于使用EclipseLink以为 entityManager.close()可以释放连接,经过查阅EclipseLink官网文档,原来是使用不当,下面是获取连接的详细信息,:

JPA 2.0
entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
...
entityManager.getTransaction().commit();

最后在finally将connection.close()。

posted @ 2017-01-05 17:12  BobZhang  阅读(669)  评论(0编辑  收藏  举报
Helloworld