系統性能優化通常分爲下面幾步走:java
一、發現問題數據庫
這一步須要先進行一些性能測試,根據測試結果找出不達標的來進一步分析優化。好比:B/S系統能夠在瀏覽器上安裝一些插件(我用的是httpwatch),打開瀏覽器後進入系統,啓動插件的功能,點擊查詢按鈕,該插件就會自動記錄查詢整個過程的耗時,能夠方便你後面的分析,下圖爲httpwatch某個查詢的耗時記錄:編程
根據性能測試結果要寫一份性能測試報告爲下一步工做提供依據,寫性能測試報告通常有如下幾點須要寫進去:數組
a、測試使用數據是否爲現場數據瀏覽器
b、測試使用的客戶端是什麼配置安全
c、服務器數據庫端的配置性能優化
d、選擇的測試條件服務器
e、每一個功能測試次數,平均耗時爲多少dom
f、每次查詢返回的數據量函數
g、數據庫裏對應表裏的數據總量
h、將測試記錄寫成文檔,對於不達標的記錄高亮顯示
二、分析問題
根據測試報告找出不達標的功能進行單獨分析,肯定存在問題的具體位置,好比是某個方法仍是某個功能段致使總體性能不能達標,通常會先大概分析一下總體的代碼,而後從外向裏添加日誌,打印出各方法的執行時間,這樣很快就能肯定出問題的具體位置,將分析結果寫入文檔,爲下一步提供依據。
三、提出方案
根據分析結果提出解決方案,而後寫成文檔提交審批。
四、解決問題
根據審批經過的文檔進行修改,而後測試修改後的結果,符合要求了就能夠提交測試部進行最總測試。
五、結果報告
測試部測試經過後一個優化就算結束了,寫好優化文檔上傳保存。
上面的這幾步每一步都很重要,可是真正難點仍是在第三步,提出解決方案,代碼優化、邏輯優化甚至是數據庫的優化都須要有豐富的編程經驗,這個不是一會兒能作到的須要長期的積累,在項目裏不斷的實踐纔可以越作越好,下面我就將一些看到過的優化方法結合本身的工做總結出來以供參考:
java代碼優化
一、循環
一般把大循環放在裏面,把小循環放在外面,例如:
for(int i=0; i<10;i++) { for(int j=0; j<1000000;j++) { ... } }
把與循環index不相關的移到循環的外面,例如:
for(int i=0; terminal=x.length;i<terminal;i++) { x = x/scaleA * scaleB; } //應該該成: Double scale = scaleB*scaleA; for(int i=0; terminal=x.length;i<terminal;i++) { x = x/scale ; }
在重要的循環裏,消除循環終止判斷時的方法調用,例如:
for(int i=0; i<collection.size();i++) { ... } //儘可能減小對變量的重複計算 for(int i=0; n=collection.size();i<n;i++) { ... }
儘可能不要在循環中使用:
Try { } catch() { }
應把其放置在最外層
循環內不要建立大量的臨時變量
for(int i=1;i<=domainCount;i++){ ... AuditResult auditResult = new AuditResult(); ... } //這種作法會在內存中保存N份這個對象的引用,會浪費大量的內存空間,改成 AuditResult auditResult; for(int i=1;i<=domainCount;i++){ ... auditResult=new AuditResult(); ... }
二、字符串
■ 消除字符串鏈接
■建立長字符串時,老是使用StringBuffter代替String
■預先分配StringBuffer空間 StringBuffer sb = new StringBuffer(5000);
■ StringBuffer 和StringBuilder的區別:
java.lang.StringBuffer線程安全的可變字符序列。一個相似於String 的字符串緩衝區,但不能修改。
StringBuilder,與該類相比,一般應該優先使用java.lang.StringBuilder類,由於它支持全部相同的操做,但因爲它不執行同步,因此速度更快。
三、異常
■ 異常只用於單個真正的錯誤條件,拋出一個異常和執行一個catch代碼塊花費是很高的(主要因爲當建立一個異常時要得到線程棧的一個快照),只當條件真的異常時才拋出一個異常
■ 拋出異常首先要建立一個新的對象。
Throwable接口的構造函數調用名爲fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆棧,收集調用跟蹤信息。
只要有異常被拋出,JVM就必須調整調用堆棧,由於在處理過程當中建立了一個新的對象。
異常只能用於錯誤處理,不該該用來控制程序流程。
■使編譯器和運行時最優化,將幾個方法調用放在一個try/catch塊中,而不是爲每一個方法調用實現幾個try/catch塊,例如:
try{ Some.method1(); //Difficut for java1.4 }catch(method1Exception e){ handle exception 1 // to optimize this code } try{ Some.method2(); //Difficut for java1.4 }catch(method2Exception e){ handle exception 2 // to optimize this code } try{ Some.method3(); //Difficut for java1.4 }catch(method3Exception e){ handle exception 3 // to optimize this code } //應該寫爲: try{ //Difficut for java1.4 Some.method1(); Some.method2(); Some.method3(); }catch(method1Exception e){ handle exception 1 }catch(method2Exception e){ handle exception 2 }catch(method3Exception e){ handle exception 3 }
四、儘可能指定類的final修飾符
■帶有final修飾符的類是不可派生的。在Java核心API中,有許多應用final的例子,例如java.lang.String。其爲String類指定final也防止了人們覆蓋length()方法。
另外,若是指定一個類爲final,則該類全部的方法都是final。Java編譯器會尋找機會內聯(inline)全部的final方法(這和具體的編譯器實現有關)。此舉可以使性能平均提升50%。
五、儘可能使用局部變量
■調用方法時傳遞的參數以及在調用中建立的臨時變量都保存在棧(Stack)中,速度較快。其餘變量,如靜態變量、實例變量等,都在堆(Heap)中建立,速度較慢。另外,依賴於具體的編譯器/JVM,局部變量還可能獲得進一步優化
六、乘法和除法
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; }
這個還須要看具體狀況,並非全部的乘除法都能這樣優化。
七、array(數組) 和 ArrayList的使用
array([]):最高效;可是其容量固定且沒法動態改變;
ArrayList:容量可動態增加;但犧牲效率;
基於效率和類型檢驗,應儘量使用array,沒法肯定數組大小時才使用ArrayList。
ArrayList內部封裝了一個Object類型的數組,從通常的意義來講,它和數組沒有本質的差異,甚至於ArrayList的許多方法,如Index、IndexOf、Contains、Sort等都是在內部數組的基礎上直接調用Array的對應方法。
八、在JAVA + ORACLE的應用系統開發中,java中內嵌的SQL語句儘可能使用大寫的形式,以減輕ORACLE解析器的解析負擔
九、因爲JVM的有其自身的GC機制,不須要程序開發者的過多考慮,從必定程度上減輕了開發者負擔,但同時也遺漏了隱患,過度的建立對象會消耗系統的大量內存,嚴重時會致使內存泄露,所以,保證過時對象的及時回收具備重要意義。JVM回收垃圾的條件是:對象不在被引用;然而,JVM的GC並不是十分的機智,即便對象知足了垃圾回收的條件也不必定會被當即回收。因此,建議咱們在對象使用完畢,應手動置成null,這樣也能夠提供性能。
十、儘可能採用lazy loading 的策略
即在須要的時候纔開始建立,例如:
String str = 「aaa」; if(i == 1) { list.add(str); } //應替換爲: if(i == 1) { String str = 「aaa」; list.add(str); }
十一、在使用同步機制時,應儘可能使用方法同步代替代碼塊同步
12、當複製大量數據時,使用System.arraycopy()命令