天天看點

Java程式優化細節

1. 盡量在合适的場合使用單例

  使用單例可以減輕加載的負擔,縮短加載的時間,提高加載的效率,但并不是所有地方都适用于單例,簡單來說,單例主要适用于以下三個方面:

    1).控制資源的使用,通過線程同步來控制資源的并發通路;

    2).控制執行個體的産生,以達到節約資源的目的;

    3).控制資料共享,在不建立直接關聯的條件下,讓多個不相關的程序或線程之間實作通信。

2. 盡量避免随意使用靜态變量

  要知道,當某個對象被定義為stataic變量所引用,那麼gc通常是不會回收這個對象所占有的記憶體,如

  Java代碼

  public class A{

  static B b = new B();

  }

  此時靜态變量b的生命周期與A類同步,如果A類不會解除安裝,那麼b對象會常駐記憶體,直到程式終止。

3. 盡量避免過多過常的建立Java對象

  盡量避免在經常調用的方法,循環中new對象,由于系統不僅要花費時間來建立對象,而且還要花時間對這些對象進行垃圾回收和處理,在我們可以控制的範圍内,最大限度的重用對象,最好能用基本的資料類型或數組來替代對象。

4. 盡量使用final修飾符

  帶有final修飾符的類是不可派生的。在Java核心API中,有許多應用final的例子,例如java.lang.String.為String類指定final防止了使用者覆寫length()方法。另外,如果一個類是final的,則該類所有方法都是final的。Java編譯器會尋找機會内聯(inline)所有的final方法(這和具體的編譯器實作有關)。此舉能夠使性能平均提高50%.

5. 盡量使用局部變量

  調用方法時傳遞的參數以及在調用中建立的臨時變量都儲存在棧(Stack)中,速度較快。其他變量,如靜态變量、執行個體變量等,都在堆(Heap)中建立,速度較慢。

6. 盡量處理好包裝類型和基本類型兩者的使用場所

  雖然包裝類型和基本類型在使用過程中是可以互相轉換,但它們兩者所産生的記憶體區域是完全不同的,基本類型資料産生和處理都在棧中處理,包裝類型是對象,是在堆中産生執行個體。

  在集合類對象,有對象方面需要的處理适用包裝類型,其他的處理提倡使用基本類型。

7. 慎用synchronized,盡量減小synchronize的方法

  都知道,實作同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,是以盡量避免無謂的同步控制。synchronize方法被調用時,直接會把目前對象鎖 了,在方法執行完之前其他線程無法調用目前對象的其他方法。是以synchronize的方法盡量小,并且應盡量使用方法同步代替代碼塊同步。

8. 盡量使用StringBuilder和StringBuffer進行字元串連接配接

9. 盡量不要使用finalize方法

  實際上,将資源清理放在finalize方法中完成是非常不好的選擇,由于GC的工作量很大,尤其是回收Young代記憶體時,大都會引起應用程式暫停,是以再選擇使用finalize方法進行資源清理,會導緻GC負擔更大,程式運作效率更差。

springmvc整合mybatis架構源碼  下載下傳位址  

10. 盡量使用基本資料類型代替對象

  String str = "hello";

  上面這種方式會建立一個"hello"字元串,而且JVM的字元緩存池還會緩存這個字元串;

  String str = new String("hello");

  此時程式除建立字元串外,str所引用的String對象底層還包含一個char[]數組,這個char[]數組依次存放了h,e,l,l,o

11. 單線程應盡量使用HashMap、ArrayList

  HashTable、Vector等使用了同步機制,降低了性能。

12. 盡量合理的建立HashMap

  當你要建立一個比較大的hashMap時,充分利用另一個構造函數

  public HashMap(int initialCapacity, float loadFactor)

  避免HashMap多次進行了hash重構,擴容是一件很耗費性能的事,在預設中initialCapacity隻有16,而loadFactor是 0.75,需要多大的容量,你最好能準确的估計你所需要的最佳大小,同樣的Hashtable,Vectors也是一樣的道理。

14. 盡量避免不必要的建立

  如

  A a = new A();

  if(i==1){list.add(a);}

  應該改為

  if(i==1){

  list.add(a);}

15. 盡量在finally塊中釋放資源

  程式中使用到的資源應當被釋放,以避免資源洩漏。這最好在finally塊中去做。不管程式執行的結果如何,finally塊總是會執行的,以確定資源的正确關閉。

16. 盡量使用移位來代替‘a/b‘的操作

  "/"是一個代價很高的操作,使用移位的操作将會更快和更有效

  int num = a / 4;

  int num = a / 8;

  int num = a 》 2;

  int num = a 》 3;

  但注意的是使用移位應添加注釋,因為移位操作不直覺,比較難了解

17.盡量使用移位來代替‘a*b‘的操作

  同樣的,對于‘*‘操作,使用移位的操作将會更快和更有效

  int num = a * 4;

  int num = a * 8;

  int num = a 《 2;

  int num = a 《 3;

18. 盡量确定StringBuffer的容量

  StringBuffer 的構造器會建立一個預設大小(通常是16)的字元數組。在使用中,如果超出這個大小,就會重新配置設定記憶體,建立一個更大的數組,并将原先的數組複制過來,再 丢棄舊的數組。在大多數情況下,你可以在建立 StringBuffer的時候指定大小,這樣就避免了在容量不夠的時候自動增長,以提高性能。

  如:StringBuffer buffer = new StringBuffer(1000);

19. 盡量早釋放無用對象的引用

  大部分時,方法局部引用變量所引用的對象 會随着方法結束而變成垃圾,是以,大部分時候程式無需将局部,引用變量顯式設為null.

  例如:

  Public void test(){

  Object obj = new Object();

  ……

  Obj=null;

  上面這個就沒必要了,随着方法test()的執行完成,程式中obj引用變量的作用域就結束了。但是如果是改成下面:

  //執行耗時,耗記憶體操作;或調用耗時,耗記憶體的方法

  這時候就有必要将obj指派為null,可以盡早的釋放對Object對象的引用。

20. 盡量避免使用二維數組

  二維資料占用的記憶體空間比一維數組多得多,大概10倍以上。

21. 盡量避免使用split

  除非是必須的,否則應該避免使用split,split由于支援正規表達式,是以效率比較低,如果是頻繁的幾十,幾百萬的調用将會耗費大量資源,如果确實需 要頻繁的調用split,可以考慮使用apache的StringUtils.split(string,char),頻繁split的可以緩存結果。

22. ArrayList & LinkedList

  一 個是線性表,一個是連結清單,一句話,随機查詢盡量使用ArrayList,ArrayList優于LinkedList,LinkedList還要移動指 針,添加删除的操作LinkedList優于ArrayList,ArrayList還要移動資料,不過這是理論性分析,事實未必如此,重要的是了解好2 者得資料結構,對症下藥。ArrayList、LinkedList差別:http://blog.csdn.net/ochangwen/article/details/51055722

23. 盡量使用System.arraycopy ()代替通過來循環複制數組

  System.arraycopy() 要比通過循環來複制數組快的多。

24. 盡量緩存經常使用的對象

  盡可能将經常使用的對象進行緩存,可以使用數組,或HashMap的容器來進行緩存,但這種方式可能導緻系統占用過多的緩存,性能下降,推薦可以使用一些第三方的開源工具,如EhCache,Oscache進行緩存,他們基本都實作了FIFO/FLU等緩存算法。

25. 盡量避免非常大的記憶體配置設定

  有時候問題不是由當時的堆狀态造成的,而是因為配置設定失敗造成的。配置設定的記憶體塊都必須是連續的,而随着堆越來越滿,找到較大的連續塊越來越困難。

26. 慎用異常

  當建立一個異常時,需要收集一個棧跟蹤(stack track),這個棧跟蹤用于描述異常是在何處建立的。建構這些棧跟蹤時需要為運作時棧做一份快照,正是這一部分開銷很大。當需要建立一個 Exception 時,JVM 不得不說:先别動,我想就您現在的樣子存一份快照,是以暫時停止入棧和出棧操作。棧跟蹤不隻包含運作時棧中的一兩個元素,而是包含這個棧中的每一個元素。

  如 果您建立一個 Exception ,就得付出代價。好在捕獲異常開銷不大,是以可以使用 try-catch 将核心内容包起來。從技術上講,您甚至可以随意地抛出異常,而不用花費很大的代價。招緻性能損失的并不是 throw 操作--盡管在沒有預先建立異常的情況下就抛出異常是有點不尋常。真正要花代價的是建立異常。幸運的是,好的程式設計習慣已教會我們,不應該不管三七二十一就 抛出異常。異常是為異常的情況而設計的,使用時也應該牢記這一原則。

  -------------------------------------------------------------------------------------------------------------

      (1)用Boolean.valueOf(boolean b)代替new Boolean()

  包裝類的記憶體占用是很恐怖的,它是基本類型記憶體占用的N倍(N>2),同時new一個對象也是性能的消耗。

  我們再看看JDK對于Boolean.valueOf(boolean b)的實作:

  Boolean類提供了兩個常量:

  public static final Boolean TRUE = new Boolean(true);

  public static final Boolean FALSE = new Boolean(false);

  而valueOf(boolean b)的内部實作是:

  return (b ? TRUE : FALSE);

  是以用Boolean.valueOf(boolean b)代替new Boolean()既能節省空間,又能提高性能。

  (2) 用Integer.valueOf(int i)代替new Integer()

  和Boolean類似,java開發中使用Integer封裝int的場合也非常多,并且通常用int表示的數值都非常小。SUN SDK中對Integer的執行個體化進行了優化,Integer類緩存了-128到127這256個狀态的Integer,如果使用 Integer.valueOf(int i),傳入的int範圍正好在此内,就傳回靜态執行個體。這樣如果我們使用Integer.valueOf代替new Integer的話也将大大降低記憶體的占用。

  (3) 用StringBuffer的append方法代替"+"進行字元串相加。

  這個已經被N多人說過N次了,這個就不多說了。

  (4)。避免過深的類層次結構和過深的方法調用。

  因為這兩者都是非常占用記憶體的(特别是方法調用更是堆棧空間的消耗大戶)。

  (5)變量隻有在用到它的時候才定義和執行個體化。

  這是初學者最容易犯的錯,合理的使用變量,并且隻有在用到它的時候才定義和執行個體化,能有效的避免記憶體空間和執行性能上的浪費,進而提高了代碼的效率。

  (6)避免在循環體中聲明建立對象,即使該對象占用記憶體空間不大。

  這種情況在我們的實際應用中經常遇到,而且我們很容易犯類似的錯誤,例如下面的代碼:

  for (int i = 0; i < 10000; ++i) {

  System.out.println("obj= " + obj);

  上面的做法會浪費較大的記憶體空間。正确的做法如下所示:

  Object obj = null;

  obj = new Object();

  System.out.println("obj= "+ obj);

  采用上面的第二種編寫方式,僅在記憶體中儲存一份對該對象的引用,而不像上面的第一種編寫方式中代碼會在記憶體中産生大量的對象引用,浪費大量的記憶體空間,而且增大了垃圾回收的負荷。是以在循環體中聲明建立對象的編寫方式應該盡量避免。

  (7)。 如果if判斷中多個條件用‘||‘或者‘&&‘連接配接,請将出現頻率最高的條件放在表達式最前面。

  這個小技巧往往能有效的提高程式的性能,尤其是當if判斷放在循環體裡面時,效果更明顯。

    1.JVM管理兩種類型的記憶體:堆記憶體(heap),棧記憶體(stack),堆内在主要用來存儲程式在運作時建立或執行個體化的對象與變量。而棧記憶體則是用來存儲程式代碼中聲明為靜态(static)(或非靜态)的方法。

    2.JVM中對象的生命周期,建立階段,應用階段,不可視階段,不可到達階段,可收集階段,終結階段,釋放階段

    4.軟引用的主要特點是具有較強的引用功能。隻有當記憶體不夠的時候,才回收這類記憶體,是以在記憶體足夠的時候,它們通常不被回收。它可以用于實作一些常用資源的緩存,實作Cache的功能

  SoftReference sr = new SoftReference(a);

  a = null;

   if(sr !=null){

  a = sr.get();

  }else{

  a = new A();

  sr = new SoftReference(a);

  5.弱引用對象與Soft引用對象最大不同就在于:GC在進行回收時,需要通過算法檢查是否回收Soft引用對象,而對于Weak引用對象,GC總是進行回收。

  WeakReference wr = new WeakReference(a);

  a = null;

  if(sr !=null){

  a = wr.get();

  wr = new WeakReference(a);  }

  6.共享靜态變量存儲空間

  7.有時候我們為了提高系統性能,避免重複耗時的操作,希望能夠重用一些建立完成的對象,利用對象池實作。類似JDBC連接配接池。

  8.瞬間值,序列化對象大變量時,如果此大變量又沒有用途,則使用transient聲明,不序列化此變量。同時網絡傳輸中也不傳輸。

  10 .(1)最基本的建議就是盡早釋放無用對象的引用

  a = null; //當使用對象a之後主動将其設定為空

  (2)盡量少用finalize函數。

  (3) 如果需要使用經常用到的圖檔展,可以使用軟引用。

  (4) 注意集合資料類型,包括數組,樹等資料,這些資料結構對GC來說,回收更為複雜,

  (5) 盡量避免在類的預設構造器中建立,初始化大量的對象,防止在調用其自類的構造器時造成不必要的記憶體資源浪費。

  (6) 盡量避免強制系統做垃圾記憶體回收。

  (7) 盡量避免顯式申請數組空間。

  (8) 盡量在合适的場景下使用對象池技術以提高系統性能,縮減系統記憶體開銷。

  11.當做數組拷貝操作時,采用System.arraycopy()方法完成拷貝操作要比采用循環的辦法完成數組拷貝操作效率高

  12. 盡量避免在循環體中調用方法,因為方法調用是比較昂貴的。

  13. 盡量避免在循環體中使用try-catch 塊,最好在循環體外使用try--catch塊以提高系統性能。

  14. 在多重循環中,如果有可能,盡量将最長的循環放在最内層,最短的循環放在最外層,以減少循環層間的變換次數。

  15. 在需要線程安全的情況下,使用List list = Collections.synchronizedList(new ArrayList());

  16. 如果預知長度,就設定ArrayList的長度。

  17. ArrayList 與 LinkedList 選擇,熟悉底層的實作原理,選擇适當的容器。

  18. 字元串累加采用StringBuffer.

  19. 系統I/O優化,采用緩沖和壓縮技術。優化性能。

  20. 避免在類在構造器的初始化其他類

  21 盡量避免在構造中對靜态變量做指派操作

  22. 不要在類的構造器中建立類的執行個體

  23. 組合優化繼承

  24. 最好通過Class.forname() 動态的裝載類

  25. JSP優化,采用out 對象中的print方法代替println()方法

  26 .采用ServletOutputStream 對象代替JSPWriter對象

  27. 采用适當的值初始化out 對象緩沖區的大小

  28. 盡量采用forward()方法重定向新的JSP

  29. 利用線程池技術處理客戶請求

  30.Servlet優化

  (1) 通過init()方法來緩存一些靜态資料以提高應用性能。

  (2) 用print() 方法取代println()方法。

  (3) 用ServletOutputStream 取代 PrintWriter.

  (4) 盡量縮小同步代碼數量

  31. 改善Servlet應用性能的方法

  (1)不要使用SingleThreadModel

  (2)使用線程池ThreadPool

  32. EJB優化

  實體EJB:

  (1)實體EJB中常用資料緩存與釋放

  (2)采用延遲加載的方式裝載關聯資料

  (3)盡可能地應用CMP類型實體EJB

  (4)直接采用JDBC技術處理大型資料

  33. 優化JDBC連接配接

  (1)設定合适的預取行值

  (2)采用連接配接池技術

  (3)全合理應用事務

  (4)選擇合适的事務隔離層與及時關閉連接配接對象

  34. PreparedStatemetn隻編譯解析一次,而Statement每次都編譯解析。

  35. 盡可能地做批處理更新

  36. 通過采用合适的getXXX方法提高系統性能

  37. 采用設計模式。