<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 55  文章 - 187  trackbacks - 0
    <2008年1月>
    303112345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    常用鏈接

    留言簿(12)

    隨筆分類

    隨筆檔案

    groovy

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    Java程序性能測試

    1 概述

    在開發(fā)中,性能測試是設(shè)計(jì)初期容易忽略的問題,開發(fā)人員會(huì)為了解決一個(gè)問題而“不擇手段”,作者所參與的項(xiàng)目中也遇到了類似問題,字符串拼接、大量的網(wǎng)絡(luò)調(diào)用和數(shù)據(jù)庫訪問等等都對(duì)系統(tǒng)的性能產(chǎn)生了影響,可是大家不會(huì)關(guān)心這些問題,“CPU速度在變快”,“內(nèi)存在變大”,并且,“好像也沒有那么慢吧”。

    有很多商業(yè)的性能測試軟件可供使用,如JprofilerJProbe Profiler等,但在開發(fā)當(dāng)中顯得有些遙遠(yuǎn)而又昂貴。

    2 目標(biāo)

    本文將講述如何利用Java語言本身提供的方法在開發(fā)中進(jìn)行性能測試,找到系統(tǒng)瓶頸,進(jìn)而改進(jìn)設(shè)計(jì);并且在盡量不修改測試對(duì)象的情況下進(jìn)行測試。

    3 預(yù)備知識(shí)

    面向?qū)ο缶幊掏ㄟ^抽象繼承采用模塊化的思想來求解問題域,但是模塊化不能很好的解決所有問題。有時(shí),這些問題可能在多個(gè)模塊中都出現(xiàn),像日志功能,為了記錄每個(gè)方法進(jìn)入和離開時(shí)的信息,你不得不在每個(gè)方法里添加log("in some method")等信息。如何解決這類問題呢?將這些解決問題的功能點(diǎn)散落在多個(gè)模塊中會(huì)使冗余增大,并且當(dāng)很多個(gè)功能點(diǎn)出現(xiàn)在一個(gè)模塊中時(shí),代碼變的很難維護(hù)。因此,AOPAspect Oriented Programming)應(yīng)運(yùn)而生。如果說OOPAobject Oriented Programming)關(guān)注的是一個(gè)類的垂直結(jié)構(gòu),那么AOP是從水平角度來看待問題。

    動(dòng)態(tài)代理類可以在運(yùn)行時(shí)實(shí)現(xiàn)若干接口,每一個(gè)動(dòng)態(tài)代理類都有一個(gè)Invocation handler對(duì)象與之對(duì)應(yīng),這個(gè)對(duì)象實(shí)現(xiàn)了InvocationHandler接口,通過動(dòng)態(tài)代理的接口對(duì)動(dòng)態(tài)代理對(duì)象的方法調(diào)用會(huì)轉(zhuǎn)而會(huì)調(diào)用Invocation handler對(duì)象的invoke方法,通過動(dòng)態(tài)代理實(shí)例、方法對(duì)象和參數(shù)對(duì)象可以執(zhí)行調(diào)用并返回結(jié)果。

    說到AOP,大家首先會(huì)想到的是日志記錄、權(quán)限檢查和事務(wù)管理,是的,AOP是解決這些問題的好辦法。本文根據(jù)AOP的思想,通過動(dòng)態(tài)代理來解決一類新的問題——性能測試(performance testing)。

    性能測試主要包括以下幾個(gè)方面:

    l         計(jì)算性能:可能是人們首先關(guān)心的,簡單的說就是執(zhí)行一段代碼所用的時(shí)間

    l         內(nèi)存消耗:程序運(yùn)行所占用的內(nèi)存大小

    l         啟動(dòng)時(shí)間:從你啟動(dòng)程序到程序正常運(yùn)行的時(shí)間

    l         可伸縮性(scalability)

    l         用戶察覺性能(perceived performance):不是程序?qū)嶋H運(yùn)行有多快,而是用戶感覺程序運(yùn)行有多快.

    本文主要給出了計(jì)算性能測試和內(nèi)存消耗測試的可行辦法。

     

    4 計(jì)算性能測試

    4.1 目標(biāo):

    通過該測試可以得到一個(gè)方法執(zhí)行需要的時(shí)間

    4.2實(shí)現(xiàn):

    Java為我們提供了System. currentTimeMillis()方法,可以得到毫秒級(jí)的當(dāng)前時(shí)間,我們?cè)谝郧暗某绦虍?dāng)中一定也寫過類似的代碼來計(jì)算執(zhí)行某一段代碼所消耗的時(shí)間。

               long start=System.currentTimeMillis();

               doSth();

               long end=System.currentTimeMillis();

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

    但是,在每個(gè)方法里面都寫上這么一段代碼是一件很枯燥的事情,我們通過Javajava.lang.reflect.Proxyjava.lang.reflect.InvocationHandler利用動(dòng)態(tài)代理來很好的解決上面的問題。

    我們要測試的例子是java.util.LinkedListjava.util.ArrayListget(int index)方法顯然ArrayList要比LinkedList高效,因?yàn)榍罢呤请S機(jī)訪問,而后者需要順序訪問。

    首先我們創(chuàng)建一個(gè)接口

    public interface Foo {

        public void testArrayList();

        public void testLinkedList();

    }

    然后我們創(chuàng)建測試對(duì)象實(shí)現(xiàn)這個(gè)接口

    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);

        }

    }

    接下來我們要做關(guān)鍵的一步,實(shí)現(xiàn)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;

        }

    }

    最后,我們創(chuàng)建測試客戶端,

    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();

            }

    }

    }

    運(yùn)行的結(jié)果如下:

    begin method testArrayList( )

    the method testArrayList lasts 0ms

    end method testArrayList

    begin method testLinkedList( )

    the method testLinkedList lasts 219ms

    end method testLinkedList

    使用動(dòng)態(tài)代理的好處是你不必修改原有代碼FooImpl,但是一個(gè)缺點(diǎn)是你不得不寫一個(gè)接口,如果你的類原來沒有實(shí)現(xiàn)接口的話。

    4.3擴(kuò)展

    在上面的例子中演示了利用動(dòng)態(tài)代理比較兩個(gè)方法的執(zhí)行時(shí)間,有時(shí)候通過一次簡單的測試進(jìn)行比較是片面的,因此可以進(jìn)行多次執(zhí)行測試對(duì)象,從而計(jì)算出最差、最好和平均性能。這樣,我們才能“加快經(jīng)常執(zhí)行的程序的速度,盡量少調(diào)用速度慢的程序”。

     

    5 內(nèi)存消耗測試

    5.1 目標(biāo)

    當(dāng)一個(gè)java應(yīng)用程序運(yùn)行時(shí),有很多需要消耗內(nèi)存的因素存在,像對(duì)象、加載類、線程等。在這里只考慮程序中的對(duì)象所消耗的虛擬機(jī)堆空間,這樣我們就可以利用Runtime 類的freeMemory()totalMemory()方法。

    5.2 實(shí)現(xiàn)

    為了方便期間,我們首先添加一個(gè)類計(jì)算當(dāng)前內(nèi)存消耗。

    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;

        }

    同時(shí)我們的測試用例也做了一下改動(dòng),測試同樣一個(gè)顯而易見的問題,比較一個(gè)長度為1000ArrayListHashMap所占空間的大小,接口、實(shí)現(xiàn)如下:

    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();

    測試結(jié)果如下:

    begin method creatArray( )

    memory increased by 4400bytes

    end method creatArray

    begin method creatHashMap( )

    memory increased by 4480bytes

    end method creatHashMap

    結(jié)果一幕了然,可以看到,我們只需要修改invoke()方法,然后簡單執(zhí)行客戶端調(diào)用就可以了。

    6 結(jié)束語

           AOP通過分解關(guān)注點(diǎn)和OOP相得益彰,使程序更加簡潔易懂,通過Java語言本身提供的動(dòng)態(tài)代理幫助我們很容易分解關(guān)注點(diǎn),取得了較好的效果。不過測試對(duì)象必須實(shí)現(xiàn)接口在一定程度上限制了動(dòng)態(tài)代理的使用,可以借鑒Spring中使用的CGlib來為沒有實(shí)現(xiàn)任何接口的類創(chuàng)建動(dòng)態(tài)代理。

    7 參考資料

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

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

    動(dòng)態(tài)代理和AOP的某些知識(shí)來自http://www.springframework.org/docs/reference/aop.html

     

    8 作者聲明

    東西寫的一般,不過是我辛勤勞動(dòng)所為,轉(zhuǎn)載請(qǐng)注明出處,可以通過whq3721@163.com 與我聯(lián)系,http://freshman.52blog.net



    本文來自:http://blog.csdn.net/freshmanya/archive/2004/11/28/196893.aspx

    筆記:
        文章寫得很好,很實(shí)用。
        采用java本身的動(dòng)態(tài)代理機(jī)制實(shí)現(xiàn)的AOP來測試實(shí)現(xiàn)了某個(gè)接口的類的各個(gè)方法的性能,很妙。
        正如作者所說的,這種方式的局限性就在于只能測試實(shí)現(xiàn)了某接口的類的測試。采用AspectJ應(yīng)該會(huì)是更好的方式。

    --------------------

        WE準(zhǔn)高手
    posted on 2008-01-24 13:13 大衛(wèi) 閱讀(1963) 評(píng)論(4)  編輯  收藏 所屬分類: Java

    FeedBack:
    # re: 找到一篇性能測試的好文,簡單實(shí)用,收藏之。 2008-01-24 18:53 王能
    那像這個(gè)NBA中文網(wǎng):http://www.yaonba.com 怎測試呢?  回復(fù)  更多評(píng)論
      
    # re: 找到一篇性能測試的好文,簡單實(shí)用,收藏之。 2008-01-25 10:31 大衛(wèi)
    @王能
    不知道你是否在開玩笑 :)
    我這里談的是針對(duì)方法的性能測試,你說的當(dāng)然需要另外的一套理論和技術(shù)。  回復(fù)  更多評(píng)論
      
    # re: 找到一篇性能測試的好文,簡單實(shí)用,收藏之。 2008-01-25 17:51 lx281
    1樓明顯是廣告……  回復(fù)  更多評(píng)論
      
    # re: 找到一篇性能測試的好文,簡單實(shí)用,收藏之。 2008-01-27 11:38 海邊沫沫
    就是,一樓在我的博客中也推薦過NBA中文網(wǎng)。
    不過這個(gè)網(wǎng)站的內(nèi)容還不錯(cuò)。

    針對(duì)整個(gè)網(wǎng)站進(jìn)行測試,當(dāng)然是集成測試和壓力測試了。  回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 亚洲福利视频一区二区| 午夜免费福利视频| 亚洲国产精品18久久久久久| 亚洲美女自拍视频| 亚洲天堂久久精品| 日韩亚洲AV无码一区二区不卡| 久久精品国产亚洲网站| 亚洲精品乱码久久久久久按摩| 亚洲精品午夜无码专区| 亚洲人成精品久久久久| 亚洲综合日韩久久成人AV| 亚洲色婷婷综合久久| 亚洲精品字幕在线观看| 亚洲AV无码精品色午夜在线观看| 国产成A人亚洲精V品无码| 国产∨亚洲V天堂无码久久久| 久久亚洲国产精品| 亚洲专区在线视频| 亚洲fuli在线观看| 亚洲人成欧美中文字幕| 国产成人人综合亚洲欧美丁香花| 亚洲欧美在线x视频| 免费人成视频在线播放| 精品一区二区三区免费视频| 免费人成激情视频在线观看冫| 久久国产精品免费看| 色影音免费色资源| 浮力影院第一页小视频国产在线观看免费 | 无码不卡亚洲成?人片| 亚洲欧洲中文日韩av乱码| 亚洲色偷偷综合亚洲AVYP| 亚洲一区综合在线播放| 中文有码亚洲制服av片| 特级一级毛片免费看| 免费毛片在线看不用播放器 | 国产在亚洲线视频观看| 一个人看的www免费在线视频| 成人精品一区二区三区不卡免费看| 一区二区在线免费观看| 好吊妞在线成人免费| 久久精品亚洲福利|