Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 447|回复: 0

[Java基础知识]Java程序性能测试

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-1 15:02:05 | 显示全部楼层 |阅读模式
    1
    1. 概述

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

    4.   

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



    6. 2 目标


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

    8.    

    9.    3 预备知识

    10.      面向对象编程通过抽象继承采用模块化的思想来求解问题域,但是模块化不能很好的解决所有问题。有时,这些问题可能在多个模块中都出现,像日志功能,为了记录每个方法进入和离开时的信息,你不得不在每个方法里添加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.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.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来为没有实现任何接口的类创建动态代理。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-4 23:34 , Processed in 0.394985 second(s), 48 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表