一、優化策略
戰略級的優化思路應在設計之初就建立,軟件初始設計,模塊,架構,很難深遠的思考性能瓶頸、模塊規模等因素。但應以適合優化的角度實施開發。
戰略優化思路1:
戰略優化思路2:
全局戰略優化措施:
1、開發框架的選擇
2、數據存儲的設計
3、模塊層級的銜接與拆分
局部戰略優化措施:
1、單一職責,單例模式
2、開閉原則,對擴展開放,對修改關閉
3、里氏替換原則,使用基類的地方可以使用子類
4、依賴倒置原則,依賴於抽象,依賴於接口
5、接口隔離原則,類間的依賴關系應該建立在最小的接口上
6、迪米特原則,最少知識,信息隱藏
二、編碼原則
1. 戰略目標
1.1 、高級可擴展的框架
可任意切換相關功能組成部分,如Struts2切換到Spring MVC,hibernate切換到mybatis等
1.2、邏輯模塊划分,分層清晰、功能及算法可替換
2. 依托戰略
2.1、一切與戰略沖突的代碼優化,都是錯誤的
2.2、編程方式將向戰略計划靠攏
3. 優化目標
3.1、更少的代碼
減少體積,編譯啟動更快
3.2、更高的效率
提高響應效率
4.、編碼時的一些建議
1、盡量指定類、方法的final修飾符——虛擬機會想辦法內聯所有的final方法來減少方法執行時創建棧幀的數量,從而降低棧溢出的風險
2、盡量重用對象——重復new對象會增加內存溢出的風險
3、盡可能使用局部變量—— 一些局部變量是在棧中,免了垃圾回收過程
4、及時關閉流——資源浪費問題
5、盡量減少對變量的重復計算——list.size()在循環中的處理,如果list很多計算多次很耗性能
6、盡量采用懶加載的策略——即在需要的時候才創建,還是創建對象問題,比如在if塊外創建了對象
7、慎用異常——異常只能用於錯誤處理,不應該用來控制程序流程。因為異常有自己的異常對象,會占用堆空間
8、不要在循環中使用try…catch…,應該把其放在最外層——因為異常有自己的異常對象,會占用堆空間
9、如果能估計到待添加的內容長度,為底層以數組方式實現的集合、工具類指定初始長度,new HashMap(256),StringBuilder(int size)——因為集合的容量是隨着元素的增多成本的擴容的
10、當復制大量數據時,使用System.arraycopy()命令,采用native
11、乘法和除法使用移位操作,乘法:<<,除法:>>——因為位運算是更接近計算機底層原理的,性能更高
12、循環內不要不斷創建對象引用,Object obj = null; for (int i = 0; i <= count; i++) { obj = new Object(); }——因為對象是占用堆空間的,在循環里面new對象,容易導致內存溢出
13、基於效率和類型檢查的考慮,應該盡可能使用array,無法確定數組大小時才使用ArrayList——因為集合的容量是隨着元素的增多成本的擴容的
14、盡量使用HashMap、ArrayList、StringBuilder,除非線程安全需要,否則不推薦使用Hashtable、Vector、StringBuffer——后三者由於使用同步機制而導致了性能開銷
15、不要將數組聲明為public static final——public不安全,而final沒有用
16、盡量在合適的場合使用單例——節省加載開銷
17、盡量避免隨意使用靜態變量——gc通常是不會回收, public class A{ private static B b = new B(); },B會在A清除后才清除
18、及時清除不再需要的會話——HttpSession的invalidate()方法清除會話。
19、實現RandomAccess接口的集合比如ArrayList,應當使用最普通的for循環而不是foreach循環來遍歷——假如是隨機訪問的,使用普通for循環效率將高於使用foreach循環;反過來,如果是順序訪問的,則使用Iterator會效率更高
20、使用同步代碼塊替代同步方法
21、將常量聲明為static final,並以大寫命名——程序啟動的時候會主動加載到常量池
22、不要創建一些不使用的對象,不要導入一些不使用的類——not use的警告,可以清除
23、程序運行過程中避免使用反射——盡量在啟動時就反射完成,大家不關心啟動花了多長時間
24、使用數據庫連接池和線程池——這個都有了,可以使用的連接池:c3p0,dbcp…
25、使用帶緩沖的輸入輸出流進行IO操作——即BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,提升IO效率
26、順序插入和隨機訪問比較多的場景使用ArrayList,元素刪除和中間插入比較多的場景使用LinkedList
27、不要讓public方法中有太多的形參——能用類就用類,不要直接使用屬性
28、字符串變量和字符串常量equals的時候將字符串常量寫在前面
29、請知道,在java中if (i == 1)和if (1 == i)是沒有區別的,但從閱讀習慣上講,建議使用前者
30、不要對數組使用toString()方法——輸出沒有意義
31、不要對超出范圍的基本數據類型做向下強制轉型——長整型不適合強轉為整型數
32、公用的集合類中不使用的數據一定要及時remove掉——因為內存泄漏問題
33、把一個基本數據類型轉為字符串,基本數據類型.toString()是最快的方式、String.valueOf(數據)次之、數據+”"最慢
34、使用最有效率的方式去遍歷Map——iterator接口
35、對資源的close()建議分開操作——分開關閉不同的資源,避免未釋放,如程序出現異常會導致后面的資源未釋放,所以最好把資源釋放放在finally中保證一定會被釋放
36、JDK7中新引入的Objects工具類,對象比較
37、避免使用正則表達式,使用Apache Commons Lang作為代替
38、遠離遞歸.遞歸會占用大量資源!
39、避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 導致的性能下降,JDK7之后,可以使用ThreadLocalRandom來獲取隨機數
40、靜態類、單例類、工廠類將它們的構造函數置為private
41、提前編譯正則表達式
42、盡可能地緩存
三、善用工具
靜態代碼分析
靜態代碼分析是指無需運行被測代碼,僅通過分析或檢查源程序的語法、結構、過程、接口等來檢查程序的正確性,找出代碼隱藏的錯誤和缺陷,如參數不匹配,有歧義的嵌套語句,錯誤的遞歸,非法計算,可能出現的空指針引用等等。
在軟件開發過程中,靜態代碼分析往往先於動態測試之前進行,同時也可以作為制定動態測試用例的參考。統計證明,在整個軟件開發生命周期中,30% 至 70% 的代碼邏輯設計和編碼缺陷是可以通過靜態代碼分析來發現和修復的。
但是,由於靜態代碼分析往往要求大量的時間消耗和相關知識的積累,因此對於軟件開發團隊來說,使用靜態代碼分析工具自動化執行代碼檢查和分析,能夠極大地提高軟件可靠性並節省軟件開發和測試成本
1、PMD工具的使用
檢查的是java源文件
主要檢查:
可能的bug——try/catch/finally/switch語句中返回空值。
死代碼——未使用的局部變量、參數、私有方法。
不理想的代碼——使用String/StringBuffer。
過於復雜的表達式——沒有必要使用if語句、while循環可以代替for循環。
重復代碼——復制/粘貼的代碼引發的bug。
PMD集成了JDeveloper, Eclipse, JEdit, JBuilder, BlueJ, CodeGuide, NetBeans,IntelliJ IDEA, TextPad, Maven, Ant, Gel, JCreator, 以及 Emacs。
2. FindBug工具的使用
FindBug是一個使用靜態方法來查找Java代碼漏洞的程序。
檢查的是.class文件
主要檢查:bytecode中的bug patterns,如code性能、空指針檢查、沒有合理關閉資源、字符串相同判錯(==,而不是equals)等
3. CheckStyle工具的使用
檢查的是java源文件
主要檢查:
javadoc注釋
命名規范
多余沒有用的import
Size度量,如過長的方法
缺少必要的空格whitespace
checkstyle的使用前提是要先定義自己的一套規范
4、其他工具的使用
Simian工具檢查代碼重復度,UADPGuarding檢查過復雜函數,上帝類
四、異步、緩存
1. 異步開發
1.1 定時器,使用間隔的查詢,合理的脫耦
舉這個例子的意思對一些的特殊的場景使用定時器,比如我們每個月要統計交易量,如果都累積到每個月的最后來統計的話,數據量很多,會導致統計很慢,這樣的話,我們就可以使用一個定時器每天晚上凌晨來統計,把結果放到一張表里面,最后到月底的時候只需要統計這張表的記錄就可以了
1.2 外部請求(短信等),使用線程池確保請求能夠快速的響應
•1)newCachedThreadPool 是一個可根據需要創建新線程的線程池,但是在以前構造的線程可用時將重用它們。對於執行很多短期異步任務的程序而言,這些線程池通常可提高程序性能。調用 execute() 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有60秒鍾未被使用的線程。因此,長時間保持空閑的線程池不會使用任何資源。注意,可以使用 ThreadPoolExecutor 構造方法創建具有類似屬性但細節不同(例如超時參數)的線程池。
•2)newSingleThreadExecutor 創建是一個單線程池,也就是該線程池只有一個線程在工作,所有的任務是串行執行的,如果這個唯一的線程因為異常結束,那么會有一個新的線程來替代它,此線程池保證所有任務的執行順序按照任務的提交順序執行。
•3)newFixedThreadPool 創建固定大小的線程池,每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小,線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。
•4)newScheduledThreadPool 創建一個大小無限的線程池,此線程池支持定時以及周期性執行任務的需求
2. 緩存及工具
堆緩存,Guava Cache、EhCache、MapDB
堆外緩存, Guava Cache、EhCache、MapDB
磁盤緩存, EhCache、MapDB
分布式緩存,Redis