一次java性能调优总结

摘要:
直到性能达到预期要求。使用BTrace监视连接池,发现只有获得的连接未被释放。它似乎属于1。

   我们的系统中新开发了一个数据抽取的功能,东西做完后,一看执行时间那叫一个恼火。参考同类系统同样功能的执行时间,目标:将本地数据处理时间压缩到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官网文档,原来是使用不当,下面是获取连接的详细信息,:

https://wiki.eclipse.org/EclipseLink/Examples/JPA/EMAPI
JPA 2.0
entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
...
entityManager.getTransaction().commit();

最后在finally将connection.close()。

免责声明:文章转载自《一次java性能调优总结》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇关于Qt creator 无法使用fcitx输入中文的问题折腾JPA,EclipseLink 缓存机制学习——树节点搜索问题引发的思考下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

BTrace使用总结

btracejvisualvmhotswap 一、背景在生产环境中可能经常遇到各种问题,定位问题需要获取程序运行时的数据信息,如方法参数、返回值、全局变量、堆栈信息等。为了获取这些数据信息,我们可以通过改写代码,增加日志信息的打印,再发布到生产环境。通过这种方式,一方面将增大定位问题的成本和周期,对于紧急问题无法做到及时响应;另一方面重新部...

sprintf与strcat

sprintf可以将整数转化为字符串,也可以连接两个字符串。但是用sprintf在连接两个字符串时,容易出现错误。 因此连接两个字符串时候用strcat, 将整数转化为字符串时候用sprintf。 转换字符: %% 印出百分比符号,不转换。   %c 整数转成对应的 ASCII 字元。    %d 整数转成十进位。    %f 倍精确度数字转成浮点数。   ...

c++ 连接两个字符串实现代码 实现类似strcat功能(转)

想实现strcat功能,直接网上找一个。 第一种: #include "stdafx.h"#include<iostream> using namespacestd; int _tmain(int argc, _TCHAR*argv[]) { char s1[60]="kingbaby"; char *s2="hello"; in...

[z]Oracle 多行记录合并/连接/聚合字符串的几种方法

详细出处参考:http://www.jb51.net/article/20948.htm 什么是合并多行字符串(连接字符串)呢,例如: SQL> desc test; Name Type Nullable Default Comments ------- ------------ -------- ------- -------- COUNTRY VA...

20160131.CCPP体系详解(0010天)

程序片段(01):Test.c+NewTest.c 内容概要:题目测试 ///Test.c #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #define r char S; T...

jni 字符串的梳理 2 字符串的处理操作

我们实现下面的一个功能: 1、首先在java层传递一个字符串到c层,c层首先将jstring转换成char*类型,然后将两个字符串相加,然后再再将char*类型转换成jstring,在上层显示出来 我们来看底层程序的代码: // // Created by wei.yuan on 2017/6/13. // #include <jni.h> #...