|
![]() |
名片设计 CorelDRAW Illustrator AuotoCAD Painter 其他软件 Photoshop Fireworks Flash |
|
1 概述 在开发中,性能测试是设计初期轻易忽略的问题,开发人员会为了解决一个问题而“不择手段”,作者所参与的项目中也碰到了类似问题,字符串拼接、大量的网络调用和数据库访问等等都对系统的性能产生了影响,可是大家不会关心这些问题,“CPU速度在变快”,“内存在变大”,并且,“似乎也没有那么慢吧”。 有很多商业的性能测试软件可供使用,如Jprofiler、JProbe Profiler等,但在开发当中显得有些遥远而又昂贵。 2 目标 本文将讲述如何利用Java语言本身提供的方式在开发中进行性能测试,找到系统瓶颈,进而改进设计;并且在尽量不修改测试对象的情况下进行测试。 3 准备知识 面向对象编程通过抽象继续采用模块化的思想来求解问题域,但是模块化不能很好的解决所有问题。有时,这些问题可能在多个模块中都出现,像日志功能,为了记录每个方式进入和离开时的信息,你不得不在每个方式里添加log("in some method")等信息。如何解决这类问题呢?将这些解决问题的功能点散落在多个模块中会使冗余增大,并且当很多个功能点出现在一个模块中时,代码变的很难维护。因此,AOP(Aspect Oriented Programming)应运而生。假如说OOP(Aobject Oriented Programming)关注的是一个类的垂直结构,那么AOP是从水平角度来看待问题。 动态代理类可以在运行时实现若干接口,每一个动态代理类都有一个Invocation handler对象与之对应,这个对象实现了InvocationHandler接口,通过动态代理的接口对动态代理对象的方式调用会转而会调用Invocation handler对象的invoke方式,通过动态代理实例、方式对象和参数对象可以执行调用并返回结果。 说到AOP,大家首先会想到的是日志记录、权限检查和事务治理,是的,AOP是解决这些问题的好办法。本文根据AOP的思想,通过动态代理来解决一类新的问题――性能测试(performance testing)。 性能测试主要包括以下几个方面: l 计算性能:可能是人们首先关心的,简朴的说就是执行一段代码所用的时间 l 内存消耗:程序运行所占用的内存大小 l 启动时间:从你启动程序到程序正常运行的时间 l 可伸缩性(scalability) l 用户察觉性能(perceived performance):不是程序实际运行有多快,而是用户感觉程序运行有多快. 本文主要给出了计算性能测试和内存消耗测试的可行办法。 4 计算性能测试 4.1 目标: 通过该测试可以得到一个方式执行需要的时间 4.2实现: Java为我们提供了System. currentTimeMillis()方式,可以得到毫秒级的当前时间,我们在以前的程序当中一定也写过类似的代码来计算执行某一段代码所消耗的时间。 long start=System.currentTimeMillis(); doSth(); long end=System.currentTimeMillis(); System.out.println("time lasts "+(end-start)+"ms"); 但是,在每个方式里面都写上这么一段代码是一件很枯燥的事情,我们通过Java的java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler利用动态代理来很好的解决上面的问题。 我们要测试的例子是java.util.LinkedList和java.util.ArrayList的get(int index)方式,显然ArrayList要比LinkedList高效,因为前者是随机访问,而后者需要顺序访问。 首先我们创建一个接口 public interface Foo { public void testArrayList(); public void testLinkedList(); } 然后我们创建测试对象实现这个接口 public class FooImpl implements Foo { private List link=new LinkedList(); private List array=new ArrayList(); public FooImpl() { for(int i=0;i<10000;i++) { array.add(new Integer(i)); link.add(new Integer(i)); } } public void testArrayList() { for(int i=0;i<10000;i++) array.get(i); } public void testLinkedList() { for(int i=0;i<10000;i++) link.get(i); } } 接下来我们要做要害的一步,实现InvocationHandler接口 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.*; public class Handler implements InvocationHandler { private Object obj; public Handler(Object obj) { this.obj = obj; } public static Object newInstance(Object obj) { Object result = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new Handler(obj)); return (result); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result; try { System.out.print("begin method " + method.getName() + "("); for (int i = 0; args != null && i < args.length; i++) { if (i > 0) System.out.print(","); System.out.print(" " + args[i].toString()); } System.out.println(" )"); long start=System.currentTimeMillis(); result = method.invoke(obj, args); long end=System.currentTimeMillis(); System.out.println("the method "+method.getName()+" lasts "+(end-start)+"ms"); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException ("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("end method " + method.getName()); } return result; } } 最后,我们创建测试客户端, public class TestProxy { public static void main(String[] args) { try { Foo foo = (Foo) Handler.newInstance(new FooImpl()); foo.testArrayList(); foo.testLinkedList(); } catch (Exception e) { e.printStackTrace(); } } } 运行的结果如下: begin method testArrayList( ) the method testArrayList lasts 0ms end method testArrayList begin method testLinkedList( ) the method testLinkedList lasts 219ms end method testLinkedList 使用动态代理的好处是你不必修改原有代码FooImpl,但是一个缺点是你不得不写一个接口,假如你的类原来没有实现接口的话。 4.3扩展 在上面的例子中演示了利用动态代理比较两个方式的执行时间,有时候通过一次简朴的测试进行比较是片面的,因此可以进行多次执行测试对象,从而计算出最差、最好和平均性能。这样,我们才能“加快常常执行的程序的速度,尽量少调用速度慢的程序”。 5 内存消耗测试 5.1 目标 当一个java应用程序运行时,有很多需要消耗内存的因素存在,像对象、加载类、线程等。在这里只考虑程序中的对象所消耗的虚拟机堆空间,这样我们就可以利用Runtime 类的freeMemory()和totalMemory()方式。 5.2 实现 为了方便期间,我们首先添加一个类计算当前内存消耗。 class Memory { public static long used() { long total=Runtime.getRuntime().totalMemory(); long free=Runtime.getRuntime().freeMemory(); return (total-free); } } 然后修改Handler类的invoke()方式。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result; try { System.out.print("begin method " + method.getName() + "("); for (int i = 0; args != null && i < args.length; i++) { if (i > 0) System.out.print(","); System.out.print(" " + args[i].toString()); } System.out.println(" )"); long start=Memory.used(); result = method.invoke(obj, args); long end=Memory.used(); System.out.println("memory increased by "+(end-start)+"bytes"); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException ("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("end method " + method.getName()); } return result; } 同时我们的测试用例也做了一下改动,测试同样一个显而易见的问题,比较一个长度为1000的ArrayList和HashMap所占空间的大小,接口、实现如下: public interface MemoConsumer { public void creatArray(); public void creatHashMap(); } public class MemoConsumerImpl implements MemoConsumer { ArrayList arr=null; HashMap hash=null; public void creatArray() { arr=new ArrayList(1000); } public void creatHashMap() { hash=new HashMap(1000); } } 测试客户端代码如下: MemoConsumer arrayMemo=(MemoConsumer)Handler.newInstance(new MemoConsumerImpl ()); arrayMemo.creatArray(); arrayMemo.creatHashMap(); 测试结果如下: begin method creatArray( ) memory increased by 4400bytes end method creatArray begin method creatHashMap( ) memory increased by 4480bytes end method creatHashMap 结果一幕了然,可以看到,我们只需要修改invoke()方式,然后简朴执行客户端调用就可以了。 6 结束语 AOP通过分解关注点和OOP相得益彰,使程序更加简洁易懂,通过Java语言本身提供的动态代理帮助我们很轻易分解关注点,取得了较好的效果。不过测试对象必须实现接口在一定程度上限制了动态代理的使用,可以借鉴Spring中使用的CGlib来为没有实现任何接口的类创建动态代理。 返回类别: 教程 上一教程: J2ME学习笔记_1_开发环境的安装和配置 下一教程: Java IO学习基础之读写文本文件 您可以阅读与"Java程序性能测试"相关的教程: · 提升Java桌面客户端程序性能 · 使用JMeter测试JSP应用程序性能 · Java程序性能优化(辛劳了几个小时,还经历了一次停电,我真是命苦!) · JAVA程序的性能优化 · 如何优化JAVA程序设计和编码,提高JAVA性能 |
![]() ![]() |
快精灵印艺坊 版权所有 |
首页![]() ![]() ![]() ![]() ![]() ![]() ![]() |