本文目錄一覽:
- 1、Java應用的五項性能優化技巧?
- 2、高並發原理 用java怎麼優化
- 3、java代碼優化
- 4、在《java性能優化之通用篇中》看到「應盡量使用方法同步代替代碼塊同步」,有疑問!
- 5、Java代碼如何優化
- 6、如何優化java程序設計和編碼,提高java性能
Java應用的五項性能優化技巧?
要想對Java應用代碼進行優化,我們首先需要分析其具體工作原理。事實上,影響性能優化效果的因素多種多樣,我們需要從垃圾回收、操作系統設置以及虛擬機制等多個角度著眼,方可順利完成任務。在今天的文章中,昆明IT培訓將分享五項性能優化技巧,希望能為你的Java應用提升工作帶來啟示。
1.從最小Heap分配入手
2.使用各類Java性能工具
3.使用StringBuilder而非+運算符
4.避免使用迭代器
5.建立更好的並發控制機制
高並發原理 用java怎麼優化
面對高並發高請求的大型JAVA應用場景,需要考慮到以下幾個方面並並進行優化:
1、代碼方面
從最基礎的做起,優化所寫代碼,減少不必要的資源浪費,比如:避免頻繁的new對象,優先考慮使用單例模式、減繁去重,重用代碼要歸納成公用方法,相關工具類使用靜態方法訪問、使用java中效率高的類等等;
2、資料庫方面
當面對複雜的應用,用戶大量訪問的時候,一台數據很快無法滿足需求,這時就需要使用資料庫集群或者庫表散列。
常用的優化措施是M-S(主-從)方式進行同步複製,將查詢和操作和分別在不同的伺服器上進行操作,這樣會大大減少資料庫操作耗時;
3、靜態資源方面
我們可以把一些訪問頻次高但是變更不大的動態請求提前渲染生成html靜態頁面,然後每次用戶再訪問該請求時,就不要再調用伺服器請求了,這樣會大大減少高峰期時伺服器的壓力;
在靜態資源例如圖片、js、css等方面,我們可以將這些資源與核心應用和html資源分離開,建立合適的靜態文件伺服器,針對不同類型的靜態資源對伺服器進行優化配置,這樣就不會再高並發時因為這些靜態資源的問題而使整個頁面崩潰了;
4、其他方面
緩存:盡量使用緩存,包括用戶緩存,信息緩存等,多花點內存來做緩存,可以大量減少與資料庫的交互,提高性能。
可以考慮memcached緩存集群和靜態HTML、Redis緩存
負載均衡:nginx(非同步)、squid(同步)、lighttpd(非同步)
存儲:分散式的,如hadop等
java代碼優化
2樓說的很好。
我補充幾個容易新手常出的問題:
例1:無用的空字元串:
如為了避免NullPointerException,新手常把字元串初始化為空字元串,如:
String s = “”;
其實這是非常不好的做法,白白創建一個無用的字元串對象,佔用資源。
例2:大量字元串拼接
如:String sql = “select * from ” + tableName + ” where + …//省略
當遇到n個字元串拼接操作的時候,必須使用StringBuffer或StringBuilder。
Java1.5以後StringBuilder更常用,因為它不考慮線程安全,性能更高。
而且在拼接的時候要注意,不要這樣:
sb.append(“aaa” + “bbb”);//不好!
要這樣:
sb.append(“aaa”).append(“bbb”);
即每個字元串都放到一個append里
不要小看字元串拼接,你如果profile一下你程序,往往會發現字元串是占內存最多的。
例3:沒有關閉資源,或沒有在finally里關閉資源:
如以下代碼,看似沒有問題:
Connection conn = …;
//用conn操作
conn.close();
但是如果在用conn操作的時候出現異常,conn.close()將不會執行,導致Connection沒有關閉。
正確寫法是:
Connection conn = null;
try {
conn = …;//建立連接
} finally {
conn.close();
}
注意這裡只是舉個例子,沒有考慮異常處理。
例4:胡亂捕獲異常,「吃掉異常」。
比如你有一個方法這樣寫:
public void test() {
try {…
} catch(SQLException e) {e.printStackTrace();}
}
看似沒什麼問題,但是如果別的程序調用這個方法時,假如這個方法出現異常,調用者如果不看日誌輸出,根本不會發現出了問題!實際上這個方法可能根本沒有執行完就被捕獲了異常,而且這個異常被這個方法「吃掉」了。造成調試困難。
其實程序員應該樹立一個觀念:永遠不要隱藏錯誤,異常/錯誤越容易暴露越好,暴露的越早越明顯越好,這樣才能儘快捕獲和改正。
所以如果沒有設計合理的異常處理機制,最簡單的,一定不要吃掉異常,要這樣寫:
public void test() throws SQLExcpetion
即把它拋出去,交給調用者來處理。如果你不想調用者處理checked Exception,可以封裝為一個RuntimeException:
public void test() {
try {
…
} catch(Exception e) {
throw new RuntimeException(e);
}
}
臨時想到的幾點,都是實際項目中經常見新手遇到的,你的老師可能不會講到,希望有所幫助。
另外多去Apache轉轉,推薦看看commons項目下的源碼,大多都很簡單但是很精緻,看看人家代碼是怎麼寫的,照貓畫虎有時候很管用的。
在《java性能優化之通用篇中》看到「應盡量使用方法同步代替代碼塊同步」,有疑問!
一間三室一廳的屋子,你用其中一室,進去後把屋門關了,其他兩室別人也
進不去了,而你進去只關了自己的房門,屋門沒關,其他人還可以用另兩室。
屋子如果是塊,室就是方法,同步的範圍在滿足需求的條件下越小越好。
Java代碼如何優化
1. 盡量在合適的場合使用單例
使用單例可以減輕載入的負擔,縮短載入的時間,提高載入的效率,但並不是所有地方都適用於單例,簡單來說,單例主要適用於以下三個方面:
第一,控制資源的使用,通過線程同步來控制資源的並發訪問;
第二,控制實例的產生,以達到節約資源的目的;
第三,控制數據共享,在不建立直接關聯的條件下,讓多個不相關的進程或線程之間實現通信。
2. 盡量避免隨意使用靜態變數
要知道,當某個對象被定義為stataic變數所引用,那麼gc通常是不會回收這個對象所佔有的內存
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負擔更大,程序運行效率更差。
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也是一樣的道理。
13. 盡量減少對變數的重複計算
並且在循環中應該避免使用複雜的表達式,在循環中,循環條件會被反覆計算,如果不使用複雜表達式,而使循環條件值不變的話,程序將會運行的更快。
14. 盡量避免不必要的創建
15. 盡量在finally塊中釋放資源
程序中使用到的資源應當被釋放,以避免資源泄漏。這最好在finally塊中去做。不管程序執行的結果如何,finally塊總是會執行的,以確保資源的正確關閉。
16. 盡量使用移位來代替’a/b’的操作
“/”是一個代價很高的操作,使用移位的操作將會更快和更有效
17.盡量使用移位來代替’a*b’的操作
同樣的,對於’*’操作,使用移位的操作將會更快和更有效
18. 盡量確定StringBuffer的容量
StringBuffer 的構造器會創建一個默認大小(通常是16)的字元數組。在使用中,如果超出這個大小,就會重新分配內存,創建一個更大的數組,並將原先的數組複製過來,再 丟棄舊的數組。在大多數情況下,你可以在創建 StringBuffer的時候指定大小,這樣就避免了在容量不夠的時候自動增長,以提高性能。
19. 盡量早釋放無用對象的引用
大部分時,方法局部引用變數所引用的對象 會隨著方法結束而變成垃圾,因此,大部分時候程序無需將局部,引用變數顯式設為null.
20. 盡量避免使用二維數組
二維數據佔用的內存空間比一維數組多得多,大概10倍以上。
21. 盡量避免使用split
除非是必須的,否則應該避免使用split,split由於支持正則表達式,所以效率比較低,如果是頻繁的幾十,幾百萬的調用將會耗費大量資源,如果確實需 要頻繁的調用split,可以考慮使用apache的StringUtils.split(string,char),頻繁split的可以緩存結果。
22. ArrayList LinkedList
一 個是線性表,一個是鏈表,一句話,隨機查詢盡量使用ArrayList,ArrayList優於LinkedList,LinkedList還要移動指 針,添加刪除的操作LinkedList優於ArrayList,ArrayList還要移動數據,不過這是理論性分析,事實未必如此,重要的是理解好2 者得數據結構,對症下藥。
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倍(N2),同時new一個對象也是性能的消耗。
(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)。 避免在循環體中聲明創建對象,即使該對象佔用內存空間不大。
這種情況在我們的實際應用中經常遇到,而且我們很容易犯類似的錯誤
採用上面的第二種編寫方式,僅在內存中保存一份對該對象的引用,而不像上面的第一種編寫方式中代碼會在內存中產生大量的對象引用,浪費大量的內存空間,而且增大了垃圾回收的負荷。因此在循環體中聲明創建對象的編寫方式應該盡量避免。
(7)。 如果if判斷中多個條件用’||’或者”連接,請將出現頻率最高的條件放在表達式最前面。
這個小技巧往往能有效的提高程序的性能,尤其是當if判斷放在循環體裡面時,效果更明顯。
1.JVM管理兩種類型的內存:堆內存(heap),棧內存(stack),堆內在主要用來存儲程序在運行時創建或實例化的對象與變數。而棧內存則是用來存儲程序代碼中聲明為靜態(static)(或非靜態)的方法。
2.JVM中對象的生命周期,創建階段,應用階段,不可視階段,不可到達階段,可收集階段,終結階段,釋放階段
3.避免在循環體中創建對象,即使該對象點用內存空間不大。
4.軟引用的主要特點是具有較強的引用功能。只有當內存不夠的時候,才回收這類內存,因此在內存足夠的時候,它們通常不被回收。它可以用於實現一些常用資源的緩存,實現Cache的功能
5.弱引用對象與Soft引用對象最大不同就在於:GC在進行回收時,需要通過演算法檢查是否回收Soft引用對象,而對於Weak引用對象,GC總是進行回收。
6.共享靜態變數存儲空間
7.有時候我們為了提高系統性能,避免重複耗時的操作,希望能夠重用一些創建完成的對象,利用對象池實現。類似JDBC連接池。
8.瞬間值,序列化對象大變數時,如果此大變數又沒有用途,則使用transient聲明,不序列化此變數。同時網路傳輸中也不傳輸。
9.不要提前創建對象
10 .(1)最基本的建議就是儘早釋放無用對象的引用
A a = new A();
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. 採用設計模式。
如何優化java程序設計和編碼,提高java性能
下面給你提供一些在JAVA程序的設計和編碼中,經常採用的一些方法和技巧,可以提高JAVA程序的性能:
1.對象的生成和大小的調整。
JAVA程序設計中一個普遍的問題就是沒有好好的利用JAVA語言本身提供的函數,從而常常會生成大量的對象(或實例)。由於系統不僅要花時間生成對象,以後可能還需花時間對這些對象進行垃圾回收和處理。因此,生成過多的對象將會給程序的性能帶來很大的影響。
例1:關於String ,StringBuffer,+和append
JAVA語言提供了對於String類型變數的操作。但如果使用不當,會給程序的性能帶來影響。如下面的語句:
String name=new String(“HuangWeiFeng”);
System.out.println(name+”is my name”);
看似已經很精簡了,其實並非如此。為了生成二進位的代碼,要進行如下的步驟和操作:
(1) 生成新的字元串 new String(STR_1);
(2) 複製該字元串;
(3) 載入字元串常量”HuangWeiFeng”(STR_2);
(4) 調用字元串的構架器(Constructor);
(5) 保存該字元串到數組中(從位置0開始);
(6) 從java.io.PrintStream類中得到靜態的out變數;
(7) 生成新的字元串緩衝變數new StringBuffer(STR_BUF_1);
(8) 複製該字元串緩衝變數;
(9) 調用字元串緩衝的構架器(Constructor);
(10) 保存該字元串緩衝到數組中(從位置1開始);
(11) 以STR_1為參數,調用字元串緩衝(StringBuffer)類中的append方法;
(12) 載入字元串常量”is my name”(STR_3);
(13) 以STR_3為參數,調用字元串緩衝(StringBuffer)類中的append方法;
(14) 對於STR_BUF_1執行toString命令;
(15) 調用out變數中的println方法,輸出結果。
由此可以看出,這兩行簡單的代碼,就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1五個對象變數。這些生成的類的實例一般都存放在堆中。堆要對所有類的超類,類的實例進行初始化,同時還要調用類極其每個超類的構架器。而這些操作都是非常消耗系統資源的。因此,對對象的生成進行限制,是完全有必要的。
經修改,上面的代碼可以用如下的代碼來替換。
StringBuffer name=new StringBuffer(“HuangWeiFeng”);
System.out.println(name.append(“is my name.”).toString());
系統將進行如下的操作:
(1) 生成新的字元串緩衝變數new StringBuffer(STR_BUF_1);
(2) 複製該字元串緩衝變數;
(3) 載入字元串常量”HuangWeiFeng”(STR_1);
(4) 調用字元串緩衝的構架器(Constructor);
(5) 保存該字元串緩衝到數組中(從位置1開始);
(6) 從java.io.PrintStream類中得到靜態的out變數;
(7) 載入STR_BUF_1;
(8) 載入字元串常量”is my name”(STR_2);
(9) 以STR_2為參數,調用字元串緩衝(StringBuffer)實例中的append方法;
(10) 對於STR_BUF_1執行toString命令(STR_3);
(11)調用out變數中的println方法,輸出結果。
由此可以看出,經過改進後的代碼只生成了四個對象變數:STR_1,STR_2,STR_3和STR_BUF_1.你可能覺得少生成一個對象不會對程序的性能有很大的提高。但下面的代碼段2的執行速度將是代碼段1的2倍。因為代碼段1生成了八個對象,而代碼段2隻生成了四個對象。
代碼段1:
String name= new StringBuffer(“HuangWeiFeng”);
name+=”is my”;
name+=”name”;
代碼段2:
StringBuffer name=new StringBuffer(“HuangWeiFeng”);
name.append(“is my”);
name.append(“name.”).toString();
因此,充分的利用JAVA提供的庫函數來優化程序,對提高JAVA程序的性能時非常重要的.其注意點主要有如下幾方面;
(1) 儘可能的使用靜態變數(Static Class Variables)
如果類中的變數不會隨他的實例而變化,就可以定義為靜態變數,從而使他所有的實例都共享這個變數。
例:
public class foo
{
SomeObject so=new SomeObject();
}
就可以定義為:
public class foo
{
static SomeObject so=new SomeObject();
}
(2) 不要對已生成的對象作過多的改變。
對於一些類(如:String類)來講,寧願在重新生成一個新的對象實例,而不應該修改已經生成的對象實例。
例:
String name=”Huang”;
name=”Wei”;
name=”Feng”;
上述代碼生成了三個String類型的對象實例。而前兩個馬上就需要系統進行垃圾回收處理。如果要對字元串進行連接的操作,性能將得更差,因為系統將不得為此生成更多得臨時變數,如上例1所示。
(3) 生成對象時,要分配給它合理的空間和大小JAVA中的很多類都有它的默認的空間分配大小。對於StringBuffer類來講,默認的分配空間大小是16個字元。如果在程序中使用StringBuffer的空間大小不是16個字元,那麼就必須進行正確的初始化。
(4) 避免生成不太使用或生命周期短的對象或變數。對於這種情況,因該定義一個對象緩衝池。以為管理一個對象緩衝池的開銷要比頻繁的生成和回收對象的開銷小的多。
(5) 只在對象作用範圍內進行初始化。JAVA允許在代碼的任何地方定義和初始化對象。這樣,就可以只在對象作用的範圍內進行初始化。從而節約系統的開銷。
例:
SomeObject so=new SomeObject();
If(x==1) then
{
Foo=so.getXX();
}
可以修改為:
if(x==1) then
{
SomeObject so=new SomeObject();
Foo=so.getXX();
}
2.異常(Exceptions)
JAVA語言中提供了try/catch來發方便用戶捕捉異常,進行異常的處理。但是如果使用不當,也會給JAVA程序的性能帶來影響。因此,要注意以下兩點:
(1) 避免對應用程序的邏輯使用try/catch
如果可以用if,while等邏輯語句來處理,那麼就儘可能的不用try/catch語句。
(2) 重用異常
在必須要進行異常的處理時,要儘可能的重用已經存在的異常對象。以為在異常的處理中,生成一個異常對象要消耗掉大部分的時間。
3. 線程(Threading)
一個高性能的應用程序中一般都會用到線程。因為線程能充分利用系統的資源。在其他線程因為等待硬碟或網路讀寫而 時,程序能繼續處理和運行。但是對線程運用不當,也會影響程序的性能。
例2:正確使用Vector類
Vector主要用來保存各種類型的對象(包括相同類型和不同類型的對象)。但是在一些情況下使用會給程序帶來性能上的影響。這主要是由Vector類的兩個特點所決定的。第一,Vector提供了線程的安全保護功能。即使Vector類中的許多方法同步。但是如果你已經確認你的應用程序是單線程,這些方法的同步就完全不必要了。第二,在Vector查找存儲的各種對象時,常常要花很多的時間進行類型的匹配。而當這些對象都是同一類型時,這些匹配就完全不必要了。因此,有必要設計一個單線程的,保存特定類型對象的類或集合來替代Vector類.用來替換的程序如下(StringVector.java):
public class StringVector
{
private String [] data;
private int count;
public StringVector()
{
this(10); // default size is 10
}
public StringVector(int initialSize)
{
data = new String[initialSize];
}
public void add(String str)
{
// ignore null strings
if(str == null) { return; }
ensureCapacity(count + 1);
data[count++] = str;
}
private void ensureCapacity(int minCapacity)
{
int oldCapacity = data.length;
if (minCapacity oldCapacity)
{
String oldData[] = data;
int newCapacity = oldCapacity * 2;
data = new String[newCapacity];
System.arraycopy(oldData, 0, data, 0, count);
}
}
public void remove(String str)
{
if(str == null) { return; // ignore null str }
for(int i = 0; i count; i++)
{
// check for a match
if(data[i].equals(str))
{
System.arraycopy(data,i+1,data,i,count-1); // copy data
// allow previously valid array element be gc′d
data[–count] = null;
return;
}
}
}
public final String getStringAt(int index)
{
if(index 0) { return null; }
else if(index count) { return null; // index is # strings }
else { return data[index]; // index is good }
}
}
因此,代碼:
Vector Strings=new Vector();
Strings.add(“One”);
Strings.add(“Two”);
String Second=(String)Strings.elementAt(1);
可以用如下的代碼替換:
StringVector Strings=new StringVector();
Strings.add(“One”);
Strings.add(“Two”);
String Second=Strings.getStringAt(1);
這樣就可以通過優化線程來提高JAVA程序的性能。用於測試的程序如下(TestCollection.java):
import java.util.Vector;
public class TestCollection
{
public static void main(String args [])
{
TestCollection collect = new TestCollection();
if(args.length == 0)
{
System.out.println(“Usage: java TestCollection [ vector | stringvector ]”);
System.exit(1);
}
if(args[0].equals(“vector”))
{
Vector store = new Vector();
long start = System.currentTimeMillis();
for(int i = 0; i 1000000; i++)
{
store.addElement(“string”);
}
long finish = System.currentTimeMillis();
System.out.println((finish-start));
start = System.currentTimeMillis();
for(int i = 0; i 1000000; i++)
{
String result = (String)store.elementAt(i);
}
finish = System.currentTimeMillis();
System.out.println((finish-start));
}
else if(args[0].equals(“stringvector”))
{
StringVector store = new StringVector();
long start = System.currentTimeMillis();
for(int i = 0; i 1000000; i++) { store.add(“string”); }
long finish = System.currentTimeMillis();
System.out.println((finish-start));
start = System.currentTimeMillis();
for(int i = 0; i 1000000; i++) {
String result = store.getStringAt(i);
}
finish = System.currentTimeMillis();
System.out.println((finish-start));
}
}
}
關於線程的操作,要注意如下幾個方面:
(1) 防止過多的同步
如上所示,不必要的同步常常會造成程序性能的下降。因此,如果程序是單線程,則一定不要使用同步。
(2) 同步方法而不要同步整個代碼段
對某個方法或函數進行同步比對整個代碼段進行同步的性能要好。
(3) 對每個對象使用多」鎖」的機制來增大並發。
一般每個對象都只有一個」鎖」,這就表明如果兩個線程執行一個對象的兩個不同的同步方法時,會發生」死鎖」。即使這兩個方法並不共享任何資源。為了避免這個問題,可以對一個對象實行」多鎖」的機制。如下所示:
class foo
{
private static int var1;
private static Object lock1=new Object();
private static int var2;
private static Object lock2=new Object();
public static void increment1()
{
synchronized(lock1)
{
var1++;
}
}
public static void increment2()
{
synchronized(lock2)
{
var2++;
}
}
}
4.輸入和輸出(I/O)
輸入和輸出包括很多方面,但涉及最多的是對硬碟,網路或資料庫的讀寫操作。對於讀寫操作,又分為有緩存和沒有緩存的;對於資料庫的操作,又可以有多種類型的JDBC驅動器可以選擇。但無論怎樣,都會給程序的性能帶來影響。因此,需要注意如下幾點:
(1) 使用輸入輸出緩衝
儘可能的多使用緩存。但如果要經常對緩存進行刷新(flush),則建議不要使用緩存。
(2) 輸出流(Output Stream)和Unicode字元串
當時用Output Stream和Unicode字元串時,Write類的開銷比較大。因為它要實現Unicode到位元組(byte)的轉換.因此,如果可能的話,在使用Write類之前就實現轉換或用OutputStream類代替Writer類來使用。
(3) 當需序列化時使用transient
當序列化一個類或對象時,對於那些原子類型(atomic)或可以重建的原素要表識為transient類型。這樣就不用每一次都進行序列化。如果這些序列化的對象要在網路上傳輸,這一小小的改變對性能會有很大的提高。
(4) 使用高速緩存(Cache)
對於那些經常要使用而又不大變化的對象或數據,可以把它存儲在高速緩存中。這樣就可以提高訪問的速度。這一點對於從資料庫中返回的結果集尤其重要。
(5) 使用速度快的JDBC驅動器(Driver)
JAVA對訪問資料庫提供了四種方法。這其中有兩種是JDBC驅動器。一種是用JAVA外包的本地驅動器;另一種是完全的JAVA驅動器。具體要使用哪一種得根據JAVA布署的環境和應用程序本身來定。
5.一些其他的經驗和技巧
(1) 使用局部變數。
(2) 避免在同一個類中動過調用函數或方法(get或set)來設置或調用變數。
(3) 避免在循環中生成同一個變數或調用同一個函數(參數變數也一樣)。
(4) 儘可能的使用static,final,private等關鍵字。
(5) 當複製大量數據時,使用System.arraycopy()命令。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/270117.html