java 调优程序的一点小心得

最近负责了一个java开发的Restful风格的webservice接口程序。程序的并发性能很低,老关注调优性能,我也来试试。

1、发现系统瓶颈。通过系统的时间打印(可使用stopWatch)来分析程序运行瓶颈。(也可以通过Jprofiler来发现占用时间多的代码或方法,有1个月试用期,之后需要付费)发现有一个里有两个方法调用占用了大量的时间,大概60%-80%。他们使用了同步关键字,同步关键字是使用了this的对象锁,这样,进入一个方法就不能进入另一个方法。这两个方法的功能是分别获取随机数,并且不能重复。其实现性能比较差,居然都写了个while(true)的循环,还在循环里查询数据库,数据多了就会在方法中占用大量的时间。这里设计有问题,需要改进。最后的改进方案是通过查询数据来获取两种ID。


2、通过1的改进后,使用loadRunner测试,性能提高了4倍。效果不明显。需要继续努力。

瓶颈发现,通过使用top、iostat、sar等命令,发现CPU异常,压力测试时居然400%(4核)(sy 53%,很异常)。后通过jstack命令,导出线程信息(jstack -l 3232(pid)),发现runable状态的大部分线程都停在了JNI调用。停止压力测试30分钟后,CPU依然400%。再次查看线程信息,还有很多线程停在了JNI调用,并且状态为runable。初步判断是某些调用JNI没有返回,并且是runable状态,导致的CPU异常。修改程序停止JNI调用,再次测试,发现CPU降到了50%。  后来经过查询tomcat的status,发现有问题的JNI调用都发现于同样的时间点(几秒内)。此问题可以确定是发生在JNI里面的c代码部分,后来经过同事的努力,修改了相应问题,解决了这个瓶颈。


瓶颈发现,通过分析业务,发现有很多基础数据表是只读表,并且会频繁的读写,非常适合缓存,就把这些表都设置成只读缓存。性能也提高了一倍。缓存设置是通过hibernate来实现的,有兴趣的同学请找相关资料。

瓶颈发现,通过分析代码,发现DAO层里,很多表都关联在了一起。于是将经常用到的表与其他表之间的关联去掉,这样查询信息就快了很多。

通过hibernate的sql打印可以看到,通过以上两步之后,与数据库的交互减少了很多。因此提高了很大的性能。


JDK调优,将-server加入到了启动参数中,结果性能提高了两倍,估计是运行时编译导致的。使用jstat查看垃圾回收信息,+PrintGCDetails打印垃圾回收内存大小。垃圾回收的比较正常(以前同事已经将内存参数设置过,没有太大问题)。

 瓶颈发现,测试报告,有个接口居然要2分钟才能运行完成,这个接口那是相当的慢啊。通过运行时查看日志信息,居然每秒都有minor GC发生,发生原因为创建的对象太多。查看代码,接口功能是要实现一个3级的List,每个对象里都有另外一个对象的List,共3级。发现接口需要查询很多信息,并且不断的从数据库里查询信息出来,并在for循环里套了for循环,然后还在里面创建对象,每个对象都很大(实体类,关联的表太多),不发生minor GC才怪呢。 此接口需要改写。新的代码将数据一次性查询出来,并根据一定的规律来排序(同老接口),然后根据数据来构建同样的3级List。 测试发现,不用1秒就能够查询出来。另外,此数据为基础数据,可以将第一次构建出来的数据直接缓存到Application或者static变量里,后面再次访问就不用再次查询了。测试发现,速度很快,与2分钟比,提高了不止10000倍。


3、通过2的改进后,使用loadRunner测试,性能提高了8倍。但通过统计系统的运行时间,结果发现是依赖的其他系统导致的,自己的程序已经没有瓶颈了,只好将此艰巨的任务交给其他的同事来跟踪了。


java调优心得:充分利用jvm和linux的工具,从文件io、网络io、cpu、内存方面来分析系统瓶颈,找到瓶颈后找相应的解决办法。另外,系统调优往往是找到一个瓶颈并且解决之后才可能会暴露隐藏在这个瓶颈之下的另一个瓶颈,掌握好jvm和linux的基础工具(有条件时选择JProfiler这样的商业工具),往往会事半功倍。

你可能感兴趣的:(java,Hibernate,webservice,jni,测试,loadrunner)