分享
 
 
 

Java程序性能测试

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Java程序性能测试

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来为没有实现任何接口的类创建动态代理。

7 参考资料

本文中提到的一些性能测试概念主要来自http://java.sun.com/docs/books/performance/

一些AOP的概念来自Jboss的http://www.jboss.org/index.html?module=html&op=userdisplay&id=developers/projects/jboss/aop

动态代理和AOP的某些知识来自http://www.springframework.org/docs/reference/aop.html

8 作者声明

东西写的一般,不过是我辛勤劳动所为,转载请注明出处,可以通过whq3721@163.com 与我联系,http://freshman.52blog.net

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有