<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

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    Java程序性能測試

    1 概述

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

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

    2 目標

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

    3 預備知識

    面向對象編程通過抽象繼承采用模塊化的思想來求解問題域,但是模塊化不能很好的解決所有問題。有時,這些問題可能在多個模塊中都出現,像日志功能,為了記錄每個方法進入和離開時的信息,你不得不在每個方法里添加log("in some method")等信息。如何解決這類問題呢?將這些解決問題的功能點散落在多個模塊中會使冗余增大,并且當很多個功能點出現在一個模塊中時,代碼變的很難維護。因此,AOPAspect Oriented Programming)應運而生。如果說OOPAobject Oriented Programming)關注的是一個類的垂直結構,那么AOP是從水平角度來看待問題。

    動態(tài)代理類可以在運行時實現若干接口,每一個動態(tài)代理類都有一個Invocation handler對象與之對應,這個對象實現了InvocationHandler接口,通過動態(tài)代理的接口對動態(tài)代理對象的方法調用會轉而會調用Invocation handler對象的invoke方法,通過動態(tài)代理實例、方法對象和參數對象可以執(zhí)行調用并返回結果。

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

    性能測試主要包括以下幾個方面:

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

    l         內存消耗:程序運行所占用的內存大小

    l         啟動時間:從你啟動程序到程序正常運行的時間

    l         可伸縮性(scalability)

    l         用戶察覺性能(perceived performance):不是程序實際運行有多快,而是用戶感覺程序運行有多快.

    本文主要給出了計算性能測試和內存消耗測試的可行辦法。

     

    4 計算性能測試

    4.1 目標:

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

    4.2實現:

    Java為我們提供了System. currentTimeMillis()方法,可以得到毫秒級的當前時間,我們在以前的程序當中一定也寫過類似的代碼來計算執(zhí)行某一段代碼所消耗的時間。

               long start=System.currentTimeMillis();

               doSth();

               long end=System.currentTimeMillis();

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

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

    我們要測試的例子是java.util.LinkedListjava.util.ArrayListget(int index)方法,顯然ArrayList要比LinkedList高效,因為前者是隨機訪問,而后者需要順序訪問。

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

    public interface Foo {

        public void testArrayList();

        public void testLinkedList();

    }

    然后我們創(chuàng)建測試對象實現這個接口

    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;

        }

    }

    最后,我們創(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();

            }

    }

    }

    運行的結果如下:

    begin method testArrayList( )

    the method testArrayList lasts 0ms

    end method testArrayList

    begin method testLinkedList( )

    the method testLinkedList lasts 219ms

    end method testLinkedList

    使用動態(tài)代理的好處是你不必修改原有代碼FooImpl,但是一個缺點是你不得不寫一個接口,如果你的類原來沒有實現接口的話。

    4.3擴展

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

     

    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;

        }

    同時我們的測試用例也做了一下改動,測試同樣一個顯而易見的問題,比較一個長度為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()方法,然后簡單執(zhí)行客戶端調用就可以了。

    6 結束語

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

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

     

    8 作者聲明

    東西寫的一般,不過是我辛勤勞動所為,轉載請注明出處,可以通過whq3721@163.com 與我聯系,http://freshman.52blog.net



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

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

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

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

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

    針對整個網站進行測試,當然是集成測試和壓力測試了。  回復  更多評論
      
    主站蜘蛛池模板: 一区二区在线免费观看| 精品亚洲成a人在线观看| 亚洲91av视频| 亚洲国产成人片在线观看| 伊人久久大香线蕉亚洲| 亚洲精品视频免费| 亚洲无码精品浪潮| 久久99亚洲综合精品首页| 国产美女亚洲精品久久久综合| 国产亚洲精品资在线| 久久久久亚洲精品男人的天堂| 久久久久亚洲av成人无码电影| 亚洲天堂在线视频| 久久亚洲精品视频| 亚洲国产成人久久精品影视| 香蕉视频在线观看亚洲| 亚洲精品影院久久久久久| 亚洲国产精品网站久久| 亚洲综合偷自成人网第页色| 亚洲成av人片在线天堂无| 在线视频亚洲一区| 一级视频在线免费观看| 成在线人视频免费视频| 国产成人AV片无码免费| 精品国产污污免费网站aⅴ| 青苹果乐园免费高清在线| 国产午夜无码视频免费网站| 亚洲国产人成精品| 亚洲精品国产精品乱码视色| 内射干少妇亚洲69XXX| 亚洲AV男人的天堂在线观看| 日韩色视频一区二区三区亚洲| 九九免费观看全部免费视频| 国产一区二区三区免费| 免费成人福利视频| 国产裸模视频免费区无码| 国产亚洲精品激情都市| 亚洲国产福利精品一区二区| 久久综合亚洲色hezyo| 美女网站在线观看视频免费的| 在线a免费观看最新网站|