`
wataxi
  • 浏览: 208987 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

找到一篇性能测试的好文,简单实用,收藏之。

    博客分类:
  • java
阅读更多

Java程序性能测试

概述

在开发中,性能测试是设计初期容易忽略的问题,开发人员会为了解决一个问题而“不择手段”,作者所参与的项目中也遇到了类似问题,字符串拼接、大量的网络调用和数据库访问等等都对系统的性能产生了影响,可是大家不会关心这些问题,“CPU速度在变快”,“内存在变大”,并且,“好像也没有那么慢吧”。

有很多商业的性能测试软件可供使用,如JprofilerJProbe Profiler等,但在开发当中显得有些遥远而又昂贵。

目标

本文将讲述如何利用Java语言本身提供的方法在开发中进行性能测试,找到系统瓶颈,进而改进设计;并且在尽量不修改测试对象的情况下进行测试。

预备知识

面向对象编程通过抽象继承采用模块化的思想来求解问题域,但是模块化不能很好的解决所有问题。有时,这些问题可能在多个模块中都出现,像日志功能,为了记录每个方法进入和离开时的信息,你不得不在每个方法里添加log("in some method")等信息。如何解决这类问题呢?将这些解决问题的功能点散落在多个模块中会使冗余增大,并且当很多个功能点出现在一个模块中时,代码变的很难维护。因此,AOPAspect Oriented Programming)应运而生。如果说OOPAobject Oriented Programming)关注的是一个类的垂直结构,那么AOP是从水平角度来看待问题。

动态代理类可以在运行时实现若干接口,每一个动态代理类都有一个Invocation handler对象与之对应,这个对象实现了InvocationHandler接口,通过动态代理的接口对动态代理对象的方法调用会转而会调用Invocation handler对象的invoke方法,通过动态代理实例、方法对象和参数对象可以执行调用并返回结果。

说到AOP,大家首先会想到的是日志记录、权限检查和事务管理,是的,AOP是解决这些问题的好办法。本文根据AOP的思想,通过动态代理来解决一类新的问题——性能测试(performance testing)。

性能测试主要包括以下几个方面:

l         计算性能:可能是人们首先关心的,简单的说就是执行一段代码所用的时间

l         内存消耗:程序运行所占用的内存大小

l         启动时间:从你启动程序到程序正常运行的时间

l         可伸缩性(scalability)

l         用户察觉性能(perceived performance):不是程序实际运行有多快,而是用户感觉程序运行有多快.

本文主要给出了计算性能测试和内存消耗测试的可行办法。

 

计算性能测试

4.1 目标:

通过该测试可以得到一个方法执行需要的时间

4.2实现:

Java为我们提供了System. currentTimeMillis()方法,可以得到毫秒级的当前时间,我们在以前的程序当中一定也写过类似的代码来计算执行某一段代码所消耗的时间。

           long start=System.currentTimeMillis();

           doSth();

           long end=System.currentTimeMillis();

           System.out.println("time lasts "+(end-start)+"ms");

但是,在每个方法里面都写上这么一段代码是一件很枯燥的事情,我们通过Javajava.lang.reflect.Proxyjava.lang.reflect.InvocationHandler利用动态代理来很好的解决上面的问题。

我们要测试的例子是java.util.LinkedListjava.util.ArrayListget(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.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;

    }

同时我们的测试用例也做了一下改动,测试同样一个显而易见的问题,比较一个长度为1000ArrayListHashMap所占空间的大小,接口、实现如下:

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()方法,然后简单执行客户端调用就可以了。

结束语

       AOP通过分解关注点和OOP相得益彰,使程序更加简洁易懂,通过Java语言本身提供的动态代理帮助我们很容易分解关注点,取得了较好的效果。不过测试对象必须实现接口在一定程度上限制了动态代理的使用,可以借鉴Spring中使用的CGlib来为没有实现任何接口的类创建动态代理。

参考资料

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

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

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

 

作者声明

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

 

分享到:
评论

相关推荐

    LoadRunner性能测试实战

    基础篇是本书的精髓部分之一,针对LoadRunner的核心组件,作者从脚本录制与开发、场景的创建与执行、性能测试结果分析三个方面进行了深入讲解。在脚本录制与开发方面,通过介绍Virtual User Generator(VuGen)的...

    #Loadrunner性能测试应用.pdf

    总的来说,《LoadRunner性能测试应用》是一本全面介绍性能测试和LoadRunner工具的实用指南,涵盖了从基础概念到高级技术的广泛内容,对于IT专业人员提升性能测试能力具有极高的参考价值。通过学习本书,读者能够掌握...

    测试精品好文

    1. **测试发展规划小议**:这篇文档可能讨论了软件测试行业的趋势和未来发展方向,比如自动化测试的普及、持续集成/持续部署(CI/CD)的重要性、以及性能测试和安全性测试在数字化时代的新挑战。它可能还包含了对个人...

    性能测试的文章,简单适用

    这篇文章将为初学者提供一个简单而实用的入门指南。 首先,我们要理解性能测试的基本概念。性能测试是一种非功能测试类型,它通过模拟不同数量的用户负载来检查系统的处理能力。这种测试可以分为几个关键类别: 1....

    C#代码性能测试类(简单实用)

    本篇将详细介绍一个简单的C#性能测试类,它可以帮助开发者方便地测试和比较代码段的执行时间,同时支持多线程测试。 该性能测试类的核心功能包括: 1. **设置执行次数**:通过`SetCount(int count)`方法,你可以...

    Web性能测试模型与性能提升策略研究(论文)

    《Web性能测试模型与性能提升策略研究》这篇论文详尽探讨了Web性能测试的重要性和实施策略,旨在确保Web应用程序的正确性和可靠性。论文分为五个章节,涵盖了从性能测试的基础概念到具体的系统设计与实现。 首先,...

    [17个软件测试文档]-3“苍蝇式的战斗精神”和“XX性能测试”

    在《[17个软件测试文档]-3“苍蝇式的战斗精神”和“XX性能测试”》这篇文档中,作者通过自己的经历分享了关于性能测试的一些心得和感悟。文档不仅表达了作者面对挑战时的心态变化,还提供了很多实用的技术建议。 ##...

    软件测试之实践篇

    《软件测试之实践篇》是一本非常实用的指南,它不仅详细介绍了性能测试的基本原理和方法,还提供了丰富的实际案例供读者参考。对于从事软件开发和测试工作的专业人员来说,这本书能够提供宝贵的指导和帮助。通过学习...

    精通软件性能测试与loadrunner实战.docx

    《精通软件性能测试与LoadRunner实战》是一本深入讲解LoadRunner性能测试工具的实用指南,旨在帮助测试人员解决脚本开发和性能测试分析两大难题。全书分为四个部分,覆盖了从理论到实践的全面知识。 入门篇首先介绍...

    nginx性能测试与优化1

    ApacheBench(ab)是Apache服务器自带的一款简单实用的HTTP服务器性能测试工具。它可以模拟多个并发用户向服务器发送请求,从而测量服务器的处理能力。在Linux系统中,通常通过`yum install httpd-tools`命令安装,...

    毕业论文---Web系统效率和性能测试的计划与实施——系统调优方法研究

    《Web系统效率和性能测试的计划与实施——系统调优方法研究》这篇毕业论文深入探讨了Web系统性能测试的关键方面,旨在通过系统调优提升Web应用的效率和性能。论文内容涵盖性能测试的基础、LoadRunner工具介绍、测试...

    性能测试 mysql 耗时分析工具

    在本篇文章中,我们将深入探讨如何使用各种工具来对MySQL进行性能测试和耗时分析。 首先,我们要理解性能测试的目标:找出数据库的瓶颈,优化SQL查询,确保数据读写速度,以及提升并发处理能力。MySQL提供了内置的...

    测试之美.pdf

    《测试之美》是一本由软件测试领域专家编撰的书籍,它集合了27位杰出测试者和开发者的23篇文章,旨在分享他们对测试工作的真知灼见、实用建议以及富有创造性的测试理念。这些文章讨论了各种测试工具、测试流程、测试...

Global site tag (gtag.js) - Google Analytics