日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

找到一篇性能測試的好文,簡單實用,收藏之。...

 鬼迷心竅 2008-01-26

Java程序性能測試

1 概述

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

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

2 目標

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

3 預備知識

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

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

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

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

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

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

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

l         可伸縮性(scalability)

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

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

 

4 計算性能測試

4.1 目標:

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

4.2實現(xiàn):

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)建測試對象實現(xiàn)這個接口

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

    }

}

接下來我們要做關鍵的一步,實現(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();

        }

}

}

運行的結果如下:

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,但是一個缺點是你不得不寫一個接口,如果你的類原來沒有實現(xiàn)接口的話。

4.3擴展

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

 

5 內存消耗測試

5.1 目標

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

5.2 實現(xiàn)

為了方便期間,我們首先添加一個類計算當前內存消耗。

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所占空間的大小,接口、實現(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();

測試結果如下:

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)代理幫助我們很容易分解關注點,取得了較好的效果。不過測試對象必須實現(xiàn)接口在一定程度上限制了動態(tài)代理的使用,可以借鑒Spring中使用的CGlib來為沒有實現(xiàn)任何接口的類創(chuàng)建動態(tài)代理。

7 參考資料

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

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

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

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多