熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

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

2013-11-23 19:43:15  來源: Java高級技術 

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