Java的性能優化實在是一個很大的話題,這裏只打算討論一些日常編碼中可以很容易做到,並且可以習慣化的一些小技巧。這些技巧的道理可能大家都懂,在一些特定的場景(比如面試的時候)也知道怎麼去做(說),但日常編碼時能做到習以爲常,自然而然就這樣處理的人,並不多。
1、Java技巧
1.1、定義公共變量,減少對象創建
當對象實例可以通過工廠類很容易獲得的時候,則容易出現這個問題,每次都創建一個實例。優化前:優化後:... LogFactory.getLog(this.class).info(msg1); ... LogFactory.getLog(this.class).info(msg2); ...
Log log = LogFactory.getLog(this.class); ... log.info(msg1); ... log.info(msg2); ...
1.2、使用StringBuilder進行字符串的拼湊
字符串相加的時候,特別是內容比較多的情況,一定記得用StringBuilder,這樣既可以減少對象的創建,也有利於垃圾回收。
1.3、在需要的時候才使用線程安全的類
在不需要線程安全的時候,儘量避免使用Vector、Hashtable等線程安全的類,而是用ArrayList、HashMap等來替換。synchronized的使用也要注意,只用在需要的方法或代碼塊上,不要濫用,甚至加到整個類上去。
1.4、善用final修飾符提高性能
final修飾變量或者修飾類,可以讓編譯器進行一些優化,甚至進行內聯(inline),大大提高代碼性能。
1.5、對循環進行優化
在大循環或循環套循環的地方,往往是代碼優化的最佳着手點。可以結合前面講的幾點進行優化,減少對象的創建、使用StringBuilder、避免使用線程安全的類等,可以顯著提高性能。
優化前:
ResultSet rs = ...; String names = ""; while (rs.next()) { names += rs.getString("userName") + ","; LogFactory.getLog(Hello.class).info(rs.getString("userName")); ... }
優化後:ResultSet rs = ...; Log log = LogFactory.getLog(Hello.class); StringBuilder names = new StringBuilder(); while (rs.next()) { String name = rs.getString("userName"); names.append(name); names.append(","); log.info(name);
}...
1.6、慎用System.out.println
不管是往控制檯(console)還是文件中輸出信息,都是比較耗資源的,調試時的信息輸出代碼,在正式發佈的時候都應該去掉。即使是用Log來做信息輸出,可以通過信息的級別來減少信息的輸出,但任然要消耗一些計算。不過用Log來代替System.out.println還算是一個best practice,你如果曾經在生產系統的Tomcat窗口上看見過滿屏飛的調試輸出,或者試圖在幾個G的日誌文件中去找一個異常的信息,就明白我的意思了......
2、JSP技巧
2.1、儘量少寫JAVA代碼
業務邏輯不要往JSP裏放,很難維護,比較理想的情況是JSP中只有<%=...%>。很多人(特別是老鳥們)可能會喜歡利用JSP的動態腳本特性來進行代碼的開發測試,這樣可以使代碼立即生效而不用重啓容器(Tomcat等),特別是在生產環境上做應急處理的時候,可能會常用這招,但在下一個版本里這些代碼都應該移到java bean去。
2.2、css、js使用獨立的文件
這個技巧也適合html,使用獨立的文件保存css、js,可以充分利用瀏覽器的緩存機制,瀏覽器在下一次訪問這個jsp的時候,可以只去取有更新的資源,從而減少網絡流量;jsp文件的縮小,也可以使Response的資源消耗變少,從而提高應用服務器的性能;在設計良好時,考慮到代碼的共享,也需要css、js文件獨立;當然了,獨立的js文件,在eclipse這樣的IDE中,也有比較好的編輯器可用,提高編碼效率;還有一個額外的好處,在對服務器進行優化的時候,可以用一個專門的HTTP服務器(比如apache)來提供css、js、圖片等靜態內容,分擔應用服務器的壓力。
2.3、不需要session時就禁用
<%page session="false"%>
2.4、避免在session中存太多對象或者存放比較大的對象
如果說無session的JSP是輕裝上陣的話,在session裏存很多對象或大對象就是鎧甲勇士了,很容易在用戶量大的時候拖垮服務器。
3、數據庫技巧
3.1、使用連接池
連接池不僅解決了數據庫建立連接很耗資源以及數據庫的連接數有限等問題,也藉助JNDI技術和commons-pool或C3P0等優秀的連接池實現給我們的應用帶來了一個好的結構,數據庫賬號信息的調整、連接數的調整、連接的異常恢復、甚至事務處理等操作都可以與具體的業務代碼分離開來,方便了部署、維護和性能調優。
3.2、記得釋放資源
如果不使用jdbcTemplate、Hibernate等框架而直接使用JDBC訪問數據庫,則一定記得要釋放資源,比較好的習慣是先寫資源釋放代碼再去實現業務邏輯。
Connection conn = getPoolConnection(...); try { ... } finally { conn.close(); }
(見過很多資源釋放的代碼,比如:if (conn!=null) try {conn.close; ...,所以,呃,儘量用jdbcTemplate這樣的模板吧)3.3、使用JDBC時儘量避免SQL重新編譯
儘量用PreparedStatement代替Statement,前者會進行預編譯,後續執行時只是參數不同而已,Statement則是RAW SQL,執行時都進行編譯。特別是循環中執行的SQL,基本上都是可以在循環外創建PreparedStatement,然後在循環中傳遞參數去執行。另外,鑑於不同數據庫的兼容總是那麼不盡如人意,所以從性能考慮,使用存儲過程也不失爲一個好主意。
String[] names = {"張三", "李四", ...}; for (int i=0; i<names.length; i++) { Statement stat = conn.createStatement(); try { String sql = "select age, sex, mobile,email from student where name='" + names[i] + "'"; ResultSet rs = stat.executeQuery(sql); ... rs.close(); } finally { stat.close(); } }
優化:String[] names = {"張三", "李四", ...}; String sql = "select age, sex, mobile,email from student where name=?"; PreparedStatement stat = conn.prepareStatement(sql); try { for (int i=0; i<names.length; i++) { stat.setString(1, names[i]); ResultSet rs = stat.executeQuery(); ... rs.close(); } } finally { stat.close(); }
注:String sql外提減少了String的創建,PreparedStatement的使用減少了數據庫的SQL編譯
3.4 優化SQL
比較常見的SQL優化技巧是建索引,減少關聯,分表,分區,使用存儲過程等
4、其它
4.1、不要濫用XML
XML就儘量用在數據交換、配置文件這些方面吧,其它能用對象就用對象,能用JSON就JSON吧
4.2、使用緩存
緩存對象或SQL查詢結果,都可以比較好提高響應速度,但會增大內存佔用,也就是以空間換時間。自己用HashMap來實現簡單的緩存或者使用比較成熟的緩存框架都可以。(這似乎是架構方面的東西,有點脫離本文討論的範圍了^_^)
(暫時就這麼多吧...)