1. gzyueqian
      18529173453
      首頁 > 新聞中心 > > 正文

      開發晉級篇:Java性能優化技巧集錦

      更新時間: 2007-05-31 14:06:03來源: 粵嵌教育瀏覽量:508


        一、Java篇

        1.1 不用new關鍵詞創建類的實例
        
        用new關鍵詞創建類的實例時,構造函數鏈中的所有構造函數都會被自動調用。但如果一個對象實現了Cloneable接口,我們可以調用它的clone()方法。clone()方法不會調用任何類構造函數。
        
        在使用設計模式(Design Pattern)的場合,如果用Factory模式創建對象,則改用clone()方法創建新的對象實例非常簡單。例如,下面是Factory模式的一個典型實現:
        
        public static Credit getNewCredit()
        {
        return new Credit();
        }
        
        改進后的代碼使用clone()方法,如下所示:
        
        private static Credit
        BaseCredit = new Credit();
        public static Credit getNewCredit()
        {
        return (Credit) BaseCredit.clone();
        }
        
        上面的思路對于數組處理同樣很有用。
        
        1.2 使用非阻塞I/O
        
        版本較低的JDK不支持非阻塞I/O API。為避免I/O阻塞,一些應用采用了創建大量線程的辦法(在較好的情況下,會使用一個緩沖池)。這種技術可以在許多必須支持并發I/O流的應用中見到,如Web服務器、報價和拍賣應用等。然而,創建Java線程需要相當可觀的開銷。
        
        JDK 1.4引入了非阻塞的I/O庫(java.nio)。如果應用要求使用版本較早的JDK,在這里有一個支持非阻塞I/O的軟件包。
        
        1.3 慎用異常
        
        異常對性能不利。拋出異常首先要創建一個新的對象。Throwable接口的構造函數調用名為fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,VM就必須調整調用堆棧,因為在處理過程中創建了一個新的對象。
        
        異常只能用于錯誤處理,不應該用來控制程序流程。
        
        1.4 不要重復初始化變量
        
        默認情況下,調用類的構造函數時, Java會把變量初始化成確定的值:所有的對象被設置成null,整數變量(byte、short、int、long)設置成0,float和double變量設置成0.0,邏輯值設置成false。當一個類從另一個類派生時,這一點尤其應該注意,因為用new關鍵詞創建一個對象時,構造函數鏈中的所有構造函數都會被自動調用。
        
        1.5 盡量指定類的final修飾符
        
        帶有final修飾符的類是不可派生的。在Java核心API中,有許多應用final的例子,例如java.lang.String。為String類指定final防止了人們覆蓋length()方法。
        
        另外,如果指定一個類為final,則該類所有的方法都是final。Java編譯器會尋找機會內聯(inline)所有的final方法(這和具體的編譯器實現有關)。此舉能夠使性能平均提高50%。
        
        1.6 盡量使用局部變量
        
        調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧(Stack)中,速度較快。其他變量,如靜態變量、實例變量等,都在堆(Heap)中創建,速度較慢。另外,依賴于具體的編譯器/JVM,局部變量還可能得到進一步優化。請參見《盡可能使用堆棧變量》。
        
        1.7 乘法和除法
        
        考慮下面的代碼:
        
        for (val = 0;
        val < 100000; val +=5)
        {
        alterX = val * 8;
        myResult = val * 2;
        }
        
        用移位操作替代乘法操作可以極大地提高性能。下面是修改后的代碼:
        
        for (val = 0;
        val < 100000;
        val += 5)
        {
        alterX = val << 3;
        myResult = val << 1;
        }
        
        修改后的代碼不再做乘以8的操作,而是改用等價的左移3位操作,每左移1位相當于乘以2。相應地,右移1位操作相當于除以2。值得一提的是,雖然移位操作速度快,但可能使代碼比較難于理解,所以加上一些注釋。
        
        二、J2EE篇
        
        前面介紹的改善性能技巧適合于大多數Java應用,接下來要討論的問題適合于使用JSP、EJB或JDBC的應用。
        
        2.1 使用緩沖標記
        
        一些應用服務器加入了面向JSP的緩沖標記功能。例如,BEA的WebLogic Server從6.0版本開始支持這個功能,Open Symphony工程也同樣支持這個功能。JSP緩沖標記既能夠緩沖頁面片斷,也能夠緩沖整個頁面。當JSP頁面執行時,如果目標片斷已經在緩沖之中,則生成該片斷的代碼就不用再執行。
        
        頁面級緩沖捕獲對指定URL的請求,并緩沖整個結果頁面。對于購物籃、目錄以及門戶網站的主頁來說,這個功能極其有用。對于這類應用,頁面級緩沖能夠保存頁面執行的結果,供后繼請求使用。
        
        對于代碼邏輯復雜的頁面,利用緩沖標記提高性能的效果比較明顯;反之,效果可能略遜一籌。
        
        2.2 始終通過會話Bean訪問實體Bean
        
        直接訪問實體Bean不利于性能。當客戶程序遠程訪問實體Bean時,每一個get方法都是一個遠程調用。訪問實體Bean的會話Bean是本地的,能夠把所有數據組織成一個結構,然后返回它的值。
        
        用會話Bean封裝對實體Bean的訪問能夠改進事務管理,因為會話Bean只有在到達事務邊界時才會提交。每一個對get方法的直接調用產生一個事務,容器將在每一個實體Bean的事務之后執行一個“裝入-讀取”操作。
        
        一些時候,使用實體Bean會導致程序性能不佳。如果實體Bean的用途就是提取和更新數據,改成在會話Bean之內利用JDBC訪問數據庫可以得到更好的性能。
        
        2.3 選擇合適的引用機制
        
        在典型的JSP應用系統中,頁頭、頁腳部分往往被抽取出來,然后根據需要引入頁頭、頁腳。當前,在JSP頁面中引入外部資源的方法主要有兩種:include指令,以及include動作。
        
        include指令:例如。該指令在編譯時引入指定的資源。在編譯之前,帶有include指令的頁面和指定的資源被合并成一個文件。被引用的外部資源在編譯時就確定,比運行時才確定資源更高效。
        
        include動作:例如。該動作引入指定頁面執行后生成的結果。由于它在運行時完成,因此對輸出結果的控制更加靈活。但時,只有當被引用的內容頻繁地改變時,或者在對主頁面的請求沒有出現之前,被引用的頁面無法確定時,使用include動作才合算。
        
        2.4 在部署描述器中設置只讀屬性
        
        實體Bean的部署描述器允許把所有get方法設置成“只讀”。當某個事務單元的工作只包含執行讀取操作的方法時,設置只讀屬性有利于提高性能,因為容器不必再執行存儲操作。
        
        2.5 緩沖對EJB Home的訪問
        
        EJB Home接口通過JNDI名稱查找獲得。這個操作需要相當可觀的開銷。JNDI查找放入Servlet的init()方法里面。如果應用中多處頻繁地出現EJB訪問,創建一個EJBHomeCache類。EJBHomeCache類一般應該作為singleton實現。
        
        2.6 為EJB實現本地接口
        
        本地接口是EJB 2.0規范新增的內容,它使得Bean能夠避免遠程調用的開銷。請考慮下面的代碼。
        
        PayBeanHome home = (PayBeanHome)
        javax.rmi.PortableRemoteObject.narrow
        (ctx.lookup ("PayBeanHome"), PayBeanHome.class);
        PayBean bean = (PayBean)
        javax.rmi.PortableRemoteObject.narrow
        (home.create(), PayBean.class);
        
        個語句表示我們要尋找Bean的Home接口。這個查找通過JNDI進行,它是一個RMI調用。然后,我們定位遠程對象,返回代理引用,這也是一個RMI調用。第二個語句示范了如何創建一個實例,涉及了創建IIOP請求并在網絡上傳輸請求的stub程序,它也是一個RMI調用。
        
        要實現本地接口,我們必須作如下修改:
        
        方法不能再拋出java.rmi.RemoteException異常,包括從RemoteException派生的異常,比如TransactionRequiredException、TransactionRolledBackException和NoSuchObjectException。
        
        EJB提供了等價的本地異常,如TransactionRequiredLocalException、TransactionRolledBackLocalException和NoSuchObjectLocalException。所有數據和返回值都通過引用的方式傳遞,而不是傳遞值。
        
        本地接口必須在EJB部署的機器上使用。簡而言之,客戶程序和提供服務的組件必須在同一個JVM上運行。如果Bean實現了本地接口,則其引用不可串行化。
        
        2.7 生成主鍵
        
        在EJB之內生成主鍵有許多途徑,下面分析了幾種常見的辦法以及它們的特點。利用數據庫內建的標識機制(SQL Server的IDENTITY或Oracle的SEQUENCE)。這種方法的缺點是EJB可移植性差。由實體Bean自己計算主鍵值(比如做增量操作)。它的缺點是要求事務可串行化,而且速度也較慢。
        
        利用NTP之類的時鐘服務。這要求有面向特定平臺的本地代碼,從而把Bean固定到了特定的OS之上。另外,它還導致了這樣一種可能,即在多CPU的服務器上,同一個毫秒之內生成了兩個主鍵。
        
        借鑒Microsoft的思路,在Bean中創建一個GUID。然而,如果不求助于JNI,Java不能確定網卡的MAC地址;如果使用JNI,則程序就要依賴于特定的OS。
        
        還有其他幾種辦法,但這些辦法同樣都有各自的局限。似乎只有一個答案比較理想:結合運用RMI和JNDI。先通過RMI注冊把RMI遠程對象綁定到JNDI樹。客戶程序通過JNDI進行查找。下面是一個例子:
        
        public class keyGenerator extends
        UnicastRemoteObject implements Remote
        {
        private static long KeyValue =
        System.currentTimeMillis();
        public static synchronized long ge

      免費預約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 在线播放亚洲最大日韩 | 一本一道DVD在线观看免费视频 | 先锋影音ⅴa中文资源 | 免费国产午夜电影视频 | 欧美亚洲国产日韩一二三区 | 日韩精品亚洲国产另类 |