局部決定整體。一個應用的整體效能取決於每個組件的效能。下面是一些協助你提高應用效能的Java編程技巧:
編程技巧 |
原因及策略 |
避免重複建立對象 |
為什麼:
- 更少的對象會需要更少的記憶體回收
- 使用的空間越少,應用的效能越好
怎麼做:
- 重複利用一個對象,而不是在每次需要的時候都去建立一個功能一樣的對象
- (這樣做)
- String s = “No longer silly”;
- (不要這樣)
- String s = new String(“silly”);
- 不可變類中既提供建構函式,又提供了靜態Factory 方法的,優先考慮使用靜態Factory 方法。
- 複用那些一旦初始化(使用靜態初始化)就不會改變的對象
|
避免循環參考 |
為什麼:
- 一組相互引用的對象,如果他們沒有被其他對象直接引用的話,它們會變得不可達,這樣會導致它們一直都保留在記憶體裡。
怎麼做:
- 你可以使用強引用來表示“父到子“的參考關聯性,使用弱引用來表示“子到父”的參考關聯性。
|
使用==操作符來替代equals(Object)方法 |
為什麼:
- ==操作符的效能更好
- 例如,對於字串比較,equals()方法會去比較字串對象裡的字元。==操作符會比較兩個對象的引用,來比較它們是否指向同一個執行個體。
怎麼做:
- 若且唯若a == b 的時候才會有a.equals(b)
- 例如,對於重複調用的地方,使用靜態Factory 方法來返回相同的對象。
|
清除無用的對象的引用 |
為什麼:
- 無用的對象引用會導致更多的記憶體回收動作,從而降低效能
- 無用的對象引用會導致更多的記憶體佔用,從而降低效能
怎麼做:
- 如果一個引用時廢棄的話,把它設定為null
- (這樣做)
-
12345678 |
public Object pop() { if (size == 0 ) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null ; // 清除無用對象的引用 return result; } |
- (不要這樣)
-
12345 |
public Object pop(){ if (size == 0 ) throw new EmptyStackException(); return elements[--size]; } |
|
避免使用finalizer |
為什麼:
- 記憶體回收行程需要單獨記錄等待終結的對象
- 調用finalize方法也有一定的開銷
- Finalizer是不安全的,因為它有可能會複活一個對象,這樣會干擾記憶體回收。
|
避免使用引用對象 |
為什麼:
- 和finalizer一樣,記憶體回收行程需要特別處理軟引用、弱引用以及幽靈引用。
- 儘管引用對象在某些方面很有作用,例如,簡化cache的實現,但是大量引用對象的存在會使得記憶體回收運行緩慢。
- 記錄一個引用對象的開銷遠遠超過一個普通對象(強引用)的開銷
|
避免使用對象池 |
為什麼:
- 對象池不僅會使得更多的資料對象保持活動,同時會使得對象的存活時間延長
- 值得注意的是,大量存活的資料對象的處理是GC的瓶頸,GC被最佳化成適合於處理許多壽命較短的對象
- 並且,建立新的對象而不是保持舊的對象存活,會對緩衝的局部性有益
- 不過,在一個包含大量大對象的環境下,例如大的數組,效能或許會因為使用對象池而有所提升。
|
選擇好的演算法和資料結構 |
為什麼:
- 考慮一下通過鏈表來實現隊列的情境
- 即使你的程式不需要遍曆整個鏈表,但是記憶體回收行程還是需要這樣做的。
- 如果元素的封裝者沒有把元素沒有把元素放在記憶體中鄰近的位置,這樣會破壞緩衝局部性。因而會導致程式長時間的暫停,尤其是對象的指標分散在一個很大的堆區時,記憶體回收行程會在標記階段追隨指標的時候頻繁遭遇緩衝失效。
|
避免使用System.gc |
為什麼:
- Java語言規範裡沒有保證調用System.gc會做什麼事情。如果它規定了的話,或許會超出你的期望,也或許每次調用都做不同的事情。
|
避免使用太多的線程 |
為什麼:
- 進程環境切換的次數會隨著要調度的進程的數目相應地增長,這樣會對效能有隱性的影響。
- 例如,Intel A-64處理器上的本地線程內容相關的大小大概是幾千KB
|
避免使用競爭鎖 |
為什麼:
- 競爭鎖一般都是程式的瓶頸,因為它的出現意味著多個線程想訪問同一個資源或者執行同一段代碼。
|
避免不需要的異常 |
為什麼:
- 異常處理會佔用一定的事件,並且會打斷程式的正常執行流程。
- 作者曾經遇到這樣一情境,在客戶的應用裡,一個正常的執行流程每秒會拋出成千上萬的NullPointerException。這個錯誤被糾正後,應用的效能裡面有了一個數量級的提升。
|
避免使用大對象 |
為什麼:
- 大對象有時候需要直接在堆而不是線上程本機存放區區(thread local areas, TLA)進行記憶體配置。
- 大對象直接在堆上分配是有壞處的,因為它會更快地產生記憶體片段。
- 在虛擬機器(例如JRockit)上分配大對象會降低效能,因為分配記憶體的時候會使用堆的全域鎖。
- 過度使用大對象會造成頻繁的全棧壓縮,這樣做是具有破壞性的,而且這樣會導致導致所有的線程暫停很長一段時間。
|