java代碼最佳化編程

來源:互聯網
上載者:User

標籤:java   lang   break   oat   sql語句   設計   inline   trace   art   

 

可供程式利用的資源(記憶體、CPU時間、網路頻寬等)是有限的,最佳化的目的就是讓程式用儘可能

少的資源完成預定的任務。最佳化通常包含兩方面的內容:減小代碼的體積,提高代碼的運行效率。

本文討論的主要是如何提高代碼的效率。
在 Java程式中,效能問題的大部分原因並不在於Java語言,而是在於程式本身。養成好的代碼編

寫習慣非常重要,比如正確地、巧妙地運用 java.lang.String類和java.util.Vector類,它能夠

顯著地提高程式的效能。下面我們就來具體地分析一下這方面的問題。

1、盡量指定類的final修飾符帶有final修飾符的類是不可派生的。在Java核心API中,有許多應用

final的例子,例如 java.lang.String。為String類指定final防止了人們覆蓋length()方法。另

外,如果指定一個類為final,則該類所有的方法都是final。Java編譯器會尋找機會內聯(inline

)所有的final方法(這和具體的編譯器實現有關)。此舉能夠使效能平均提高 50% 。

2、量重用對象。特別是String 對象的使用中,出現字串串連情況時應用StringBuffer 代替。

由於系統不僅要花時間產生對象,以後可能還需花時間對這些對象進行記憶體回收和處理。因此,生

成過多的對象將會給程式的效能帶來很大的影響。

3、盡量使用局部變數,調用方法時傳遞的參數以及在調用中建立的臨時變數都儲存在棧(Stack)

中,速度較快。其他變數,如靜態變數、執行個體變數等,都在堆(Heap)中建立,速度較慢。另外,

依賴於具體的編譯器/JVM,局部變數還可能得到進一步最佳化。請參見《儘可能使用堆棧變數》。

4、不要重複初始設定變數  預設情況下,調用類的建構函式時, Java會把變數初始化成確定的值:

所有的對象被設定成null,整數變數(byte、 short、int、long)設定成0,float和double變數

設定成0.0,邏輯值設定成false。當一個類從另一個類派生時,這一點尤其應該注意,因為用new

關鍵詞建立一個對象時,建構函式鏈中的所有建構函式都會被自動調用。

5、在JAVA + ORACLE 的應用系統開發中,java中內嵌的SQL語句盡量使用大寫的形式,以減輕

ORACLE解析器的解析負擔。

6、Java 編程過程中,進行資料庫連接、I/O流操作時務必小心,在使用完畢後,及時關閉以釋放

資源。因為對這些大對象的操作會造成系統大的開銷,稍有不慎,會導致嚴重的後果。

7、由於JVM的有其自身的GC機制,不需要程式開發人員的過多考慮,從一定程度上減輕了開發人員負擔

,但同時也遺漏了隱患,過分的建立對象會消耗系統的大量記憶體,嚴重時會導致記憶體泄露,因此,

保證到期對象的及時回收具有重要意義。JVM回收垃圾的條件是:對象不在被引用;然而,JVM的GC

並非十分的機智,即使對象滿足了記憶體回收的條件也不一定會被立即回收。所以,建議我們在對象

使用完畢,應手動置成null。

8、在使用同步機制時,應盡量使用方法同步代替代碼塊同步。

9、盡量減少對變數的重複計算

  例如:for(int i = 0;i < list.size; i ++) {

  …

  }

  應替換為:

  for(int i = 0,int len = list.size();i < len; i ++) {

  …

  }

10、盡量採用lazy loading 的策略,即在需要的時候才開始建立。

  例如:    String str = “aaa”;

  if(i == 1) {

  list.add(str);

  }

  應替換為:

  if(i == 1) {

  String str = “aaa”;

  list.add(str);

  }

11、慎用異常
  異常對效能不利。拋出異常首先要建立一個新的對象。Throwable介面的建構函式調用名為

fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆棧,收集調用跟蹤信

息。只要有異常被拋出,VM就必須調整呼叫堆疊,因為在處理過程中建立了一個新的對象。 異常

只能用於錯誤處理,不應該用來控製程序流程。

12、不要在迴圈中使用:

  Try {

  } catch() {

  }

  應把其放置在最外層。

13、設定StringBuffer的初始化容量

  StringBuffer表示了可變的、可寫的字串。

  有三個構造方法 :

  StringBuffer ();            //預設分配16個字元的空間

  StringBuffer (int size);  //分配size個字元的空間

  StringBuffer (String str);  //分配16個字元+str.length()個字元空間

  你可以通過StringBuffer的建構函式來設定它的初始化容量,這樣可以明顯地提升效能。這裡

提到的建構函式是 StringBuffer(int length),length參數表示當前的StringBuffer能保持的字

符數量。你也可以使用 ensureCapacity(int minimumcapacity)方法在StringBuffer對象建立之後

設定它的容量。首先我們看看 StringBuffer的預設行為,然後再找出一條更好的提升效能的途徑

  StringBuffer在內部維護一個字元數組,當你使用預設的建構函式來建立StringBuffer對象的

時候,因為沒有設定初始化字元長度,StringBuffer的容量被初始化為16個字元,也就是說預設容

量就是16個字元。當StringBuffer達到最大容量的時候,它會將自身容量增加到當前的2倍再加2,

也就是(2*舊值+2)。如果你使用預設值,初始化之後接著往裡面追加字元,在你追加到第16個字

符的時候它會將容量增加到34(2*16+2),當追加到34個字元的時候就會將容量增加到 70

(2*34+2)。無論何事只要StringBuffer到達它的最大容量它就不得不建立一個新的字元數組然後

重新將舊字元和新字元都拷貝一遍――這也太昂貴了點。所以總是給StringBuffer設定一個合理的

初始化容量值是錯不了的,這樣會帶來立竿見影的效能增益。

  StringBuffer初始化過程的調整的作用由此可見一斑。所以,使用一個合適的容量值來初始化

StringBuffer永遠都是一個最佳的建議。

14、合理的使用Java類 java.util.Vector。

  簡單地說,一個Vector就是一個java.lang.Object執行個體的數組。Vector與數組相似,它的元素

可以通過整數形式的索引訪問。但是,Vector類型的對象在建立之後,對象的大小能夠根據元素的

增加或者刪除而擴充、縮小。請考慮下面這個向Vector加入元素的例子:

  Object obj = new Object();

  Vector v = new Vector(100000);

  for(int I=0;

  I<100000; I++) { v.add(0,obj); }

  除非有絕對充足的理由要求每次都把新元素插入到Vector的前面,否則上面的代碼對效能不利

。在預設建構函式中,Vector的初始儲存能力是10個元素,如果新元素加入時儲存能力不足,則以

後儲存能力每次加倍。Vector類就象StringBuffer類一樣,每次擴充儲存能力時,所有現有的元素

都要複製到新的儲存空間之中。下面的程式碼片段要比前面的例子快幾個數量級:

  Object obj = new Object();

  Vector v = new Vector(100000);

  for(int I=0; I<100000; I++) { v.add(obj); }

  同樣的規則也適用於Vector類的remove()方法。由於Vector中各個元素之間不能含有“空隙”

,刪除除最後一個元素之外的任意其他元素都導致被刪除元素之後的元素向前移動。也就是說,從

Vector刪除最後一個元素要比刪除第一個元素“開銷”低好幾倍。
假設要從前面的Vector刪除所有元素,我們可以使用這種代碼:

  for(int I=0; I<100000; I++)

  {

  v.remove(0);

  }

  但是,與下面的代碼相比,前面的代碼要慢幾個數量級:

  for(int I=0; I<100000; I++)

  {

  v.remove(v.size()-1);

  }

  從Vector類型的對象v刪除所有元素的最好方法是:

  v.removeAllElements();

假設Vector類型的對象v包含字串“Hello”。考慮下面的代碼,它要從這個Vector中刪除

“Hello”字串:

  String s = "Hello";

  int i = v.indexOf(s);

  if(I != -1) v.remove(s);

  這些代碼看起來沒什麼錯誤,但它同樣對效能不利。在這段代碼中,indexOf()方法對v進行順

序搜尋尋找字串“Hello”,remove(s)方法也要進行同樣的順序搜尋。改進之後的版本是:

  String s = "Hello";

  int i = v.indexOf(s);

  if(I != -1) v.remove(i);

  這個版本中我們直接在remove()方法中給出待刪除元素的精確索引位置,從而避免了第二次搜

索。一個更好的版本是:

  String s = "Hello"; v.remove(s);

最後,我們再來看一個有關Vector類的程式碼片段:

  for(int I=0; I++;I < v.length)

  如果v包含100,000個元素,這個程式碼片段將調用v.size()方法100,000次。雖然size方法是一

個簡單的方法,但它仍舊需要一次方法調用的開銷,至少JVM需要為它配置以及清除堆棧環境。在

這裡,for迴圈內部的代碼不會以任何方式修改Vector類型對象v的大小,因此上面的代碼最好改寫

成下面這種形式:

  int size = v.size(); for(int I=0; I++;I<size)< p="" style="word-wrap: break-word;"></size)<>

  雖然這是一個簡單的改動,但它仍舊贏得了效能。畢竟,每一個CPU周期都是寶貴的。

15、當複製大量資料時,使用System.arraycopy()命令。

16、代碼重構:增強代碼的可讀性。

  例如:

  public class ShopCart {

  private List carts ;

  …

  public void add (Object item) {

  if(carts == null) {

  carts = new ArrayList();

  }

  crts.add(item);

  }

  public void remove(Object item) {

  if(carts. contains(item)) {

  carts.remove(item);

  }

  }

  public List getCarts() {

  //返回唯讀列表

  return Collections.unmodifiableList(carts);

  }

  //不推薦這種方式

  //this.getCarts().add(item);

  }

17、不用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();

  }

  上面的思路對於數組處理同樣很有用。

18、乘法和除法

  考慮下面的代碼:

  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。值得一提的是,雖然移位操作速度快,但可能使代碼比較難於理

解,所以最好加上一些注釋。

19、在JSP頁面中關閉無用的會話。

  一個常見的誤解是以為session在有用戶端訪問時就被建立,然而事實是直到某server端程式

調用 HttpServletRequest.getSession(true)這樣的語句時才被建立,注意如果JSP沒有顯示的使

用 <%@page session="false"%> 關閉session,則JSP檔案在編譯成Servlet時將會自動加上這樣一

條語句HttpSession session = HttpServletRequest.getSession(true);這也是JSP中隱含的

session對象的來曆。由於session會消耗記憶體資源,因此,如果不打算使用session,應該在所有

的JSP中關閉它。

對於那些無需跟蹤工作階段狀態的頁面,關閉自動建立的會話可以節省一些資源。使用如下page指令:

<%@ page session="false"%>
20、JDBC與I/O

  如果應用程式需要訪問一個規模很大的資料集,則應當考慮使用塊提取方式。預設情況下,

JDBC每次提取32行資料。舉例來說,假設我們要遍曆一個5000行的記錄集,JDBC必須調用資料庫

157次才能提取到全部資料。如果把塊大小改成512,則調用資料庫的次數將減少到10次。

21、Servlet與記憶體使用量

  許多開發人員隨意地把大量資訊儲存到使用者會話之中。一些時候,儲存在會話中的對象沒有及時

地被記憶體回收機制回收。從效能上看,典型的癥狀是使用者感到系統周期性地變慢,卻又不能把原因

歸於任何一個具體的組件。如果監視JVM的堆空間,它的表現是記憶體佔用不正常地大起大落。

想要學習前端開發的同學,可以加群:543627393 學習哦!

java代碼最佳化編程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.