3.2 字符串的調(diào)優(yōu)
下面列出一些常見(jiàn)的關(guān)于字符串優(yōu)化的策略,簡(jiǎn)單的我就不多作解釋了。
1) 使用規(guī)則表達(dá)式處理字符串匹配代替復(fù)雜的字符串查找和復(fù)制操作;
2) 使用不拷貝字符串中字符的高效方法,例如String.subString()方法;
3) 盡可能不要使用需要拷貝字符串中字符的低效方法,例如String.toUpperCase()和String.toLowercase();
4) 在編譯期使用String的“+”操作符來(lái)執(zhí)行連接操作,在運(yùn)行期使用StringBuffer執(zhí)行連接操作;
這里特別強(qiáng)調(diào)一下,因?yàn)槲乙呀?jīng)在網(wǎng)上看到好多文章都推薦使用StringBuffer的append()方法來(lái)做字符串的連接操作。其實(shí)在JVM能夠在編譯期就能確定結(jié)果的情形,使用String的“+”操作符的性能要好很多。
3.3 異常,類(lèi)型轉(zhuǎn)換和變量
1) 考慮在拋出異常的時(shí)候是否可以不即時(shí)生成堆棧信息而使用一個(gè)已有的異常實(shí)例;
創(chuàng)建異常的開(kāi)銷(xiāo)很大。當(dāng)創(chuàng)建一個(gè)異常時(shí),需要收集一個(gè)棧跟蹤(Stack Trace),這個(gè)棧跟蹤用于描述異常是在何處創(chuàng)建的。構(gòu)建這些棧跟蹤時(shí)需要為運(yùn)行時(shí)棧做一份快照,正是這一部分開(kāi)銷(xiāo)很大。運(yùn)行時(shí)棧不是為有效的異常創(chuàng)建而設(shè)計(jì)的,而是設(shè)計(jì)用來(lái)讓運(yùn)行時(shí)盡可能快地運(yùn)行。入棧,出棧,入棧,出棧。讓這樣的工作順利完成,而沒(méi)有任何不必要的延遲。但是,當(dāng)需要?jiǎng)?chuàng)建一個(gè)Exception時(shí),JVM不得不說(shuō):“先別動(dòng),我想就你現(xiàn)在的樣子存一份快照,所以按時(shí)停止入棧和出棧操作,笑著等我拍完快照吧。”棧跟蹤不只包含運(yùn)行時(shí)棧中的一兩個(gè)元素,而是包含這個(gè)棧中的每一個(gè)元素,從棧頂?shù)綏5祝€有行號(hào)和一切應(yīng)有的東西。
因此,創(chuàng)建異常這一部分開(kāi)銷(xiāo)很大。從技術(shù)上講,棧跟蹤快照是在本地方法Throwable.fillInStackTrace()中發(fā)生的,這個(gè)方法不是從Throwable contructor那里調(diào)用的。但是這并并沒(méi)有什么影響——如果你創(chuàng)建了一個(gè)Exception,就得付出代價(jià)。好在捕獲異常開(kāi)銷(xiāo)不大,因此可以用try-catch將核心內(nèi)容包起來(lái)。你也可以在方法定義中定義throws子句,這樣對(duì)性能不會(huì)造成什么損失。從技術(shù)上講,你甚至可以隨意地拋出異常,而不用花費(fèi)很大的代價(jià)。招致性能損失的并不是throw操作——盡管在沒(méi)有預(yù)先創(chuàng)建異常的情況下就拋出異常是有點(diǎn)不尋常。真正要花代價(jià)的是創(chuàng)建異常。
幸運(yùn)的是,好的編程習(xí)慣已教會(huì)我們,不應(yīng)該不管三七二十一就拋出異常。異常是為異常的情況而設(shè)計(jì)的,使用時(shí)也應(yīng)該牢記這一原則。但是,萬(wàn)一你不想遵從好的編程習(xí)慣,Java語(yǔ)言就會(huì)讓你知道,那樣就可以讓你的程序運(yùn)行的更快,從而鼓勵(lì)你去那樣做。
2) 用instanceof替代在try-catch中做投機(jī)的強(qiáng)制類(lèi)型轉(zhuǎn)換方法;
3) 盡可能少的使用強(qiáng)制類(lèi)型轉(zhuǎn)換方法,尤其是使用類(lèi)型特定的集合類(lèi)時(shí);
4) 使用int優(yōu)先于其他所有的數(shù)據(jù)類(lèi)型;
5) 盡可能使用基本數(shù)據(jù)類(lèi)型做臨時(shí)變量;
6) 考慮直接獲取實(shí)例變量而不通過(guò)get,set方法獲取(注意:這不符合面向?qū)ο蟮姆庋b原則,不推薦使用)。
3.4 循環(huán),選擇和遞歸
1) 在循環(huán)中消除不必要的代碼,做盡可能少的事情;
2) Switch語(yǔ)句中使用連續(xù)的case值;
3) 確定是否真的需要用到遞歸,最好轉(zhuǎn)為用循環(huán)來(lái)實(shí)現(xiàn)。
3.5 輸入輸出操作
1) 在程序中盡量不要使用System.out這樣的語(yǔ)句,而使用log4j這樣的日志工具替換,以在程序正式上線的時(shí)候可以關(guān)閉所有不必要的日志操作提高性能;
2) 當(dāng)程序中有大量的I/O操作時(shí),考慮將日志寫(xiě)入不同的文件做到并行化操作以提高性能,并可以用一個(gè)后臺(tái)線程執(zhí)行I/O操作而不打斷正常程序的執(zhí)行;
3) 正確的使用序列化機(jī)制,沒(méi)有必要序列化的成員變量需要標(biāo)識(shí)為transient;
4) 使用NIO技術(shù)。
1) 使用正確的JDBC驅(qū)動(dòng),盡可能地選擇最新的JDBC驅(qū)動(dòng);
最新的JDBC驅(qū)動(dòng)不僅優(yōu)化了性能,而且提供了更多的性能更好的接口供開(kāi)發(fā)人員使用。
2) 使用應(yīng)用服務(wù)器自帶的連接池,而不要使用自己的連接池或干脆不用連接池;
3) 在使用完數(shù)據(jù)庫(kù)資源后,需依次關(guān)閉ResultSet,Statement和Connection;
4) 手動(dòng)控制事務(wù),使用connection.setAutoCommit(false)關(guān)閉自動(dòng)提交,使用executeBatch()進(jìn)行批量更新;
5) 業(yè)務(wù)復(fù)雜或者大數(shù)據(jù)量操作時(shí)使用存儲(chǔ)過(guò)程;
6) ResultSet.next()極其消耗性能,建議使用RowSet替代ResultSet;
7) 把所有的字符數(shù)據(jù)都保存為Unicode,Java以UniCode形式處理所有數(shù)據(jù),數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序不必再執(zhí)行轉(zhuǎn)換過(guò)程;
8) 盡可能的優(yōu)化SQL語(yǔ)句;
9) 少用join,多用index;
10) 使用EXPLAIN工具監(jiān)控SQL語(yǔ)句的執(zhí)行,以確定瓶頸所在;
11) 不要使用 SELECT * ..., 使用 SELECT Field1, Field1 ...;
12) 通過(guò)index獲取字段,而不要使用名字去獲取,例如resultSet.getString(1) 而不是 resultSet.getString("field1");
13) 緩存數(shù)據(jù),避免重復(fù)查詢(xún);
14) 考慮使用內(nèi)存數(shù)據(jù)庫(kù);
15) 調(diào)整fetch size進(jìn)行批量查詢(xún);
16) 盡可能的使Java數(shù)據(jù)類(lèi)型和數(shù)據(jù)庫(kù)類(lèi)型相匹配,轉(zhuǎn)換數(shù)據(jù)在匹配不好的數(shù)據(jù)類(lèi)型間效率太差;
17) 避免使用低效的metadata調(diào)用,尤其是getBestRowIdentifier( ), getColumns( ), getCrossReference( ), getExportedKeys( ), getImportedKeys( ), getPrimaryKeys( ), getTables( ), and getVersionColumns( );
18) 使用metadata查詢(xún)減少數(shù)據(jù)庫(kù)網(wǎng)絡(luò)通信量;
19) 使用最低的事務(wù)隔離級(jí)別;
20) 使用樂(lè)觀鎖機(jī)制;
21) 把應(yīng)用服務(wù)器和數(shù)據(jù)庫(kù)分散在不同的機(jī)器中,性能可能會(huì)更好。
1) Session的使用;
應(yīng)用服務(wù)器保存很多會(huì)話(huà)時(shí),容易造成內(nèi)存不足,所以盡量減少Session的使用,放置到Session中的對(duì)象不應(yīng)該是大對(duì)象,最好是簡(jiǎn)單的小對(duì)象,實(shí)現(xiàn)串行化接口。當(dāng)會(huì)話(huà)不再需要時(shí),應(yīng)當(dāng)及時(shí)調(diào)用invalidate()方法清除會(huì)話(huà)。而當(dāng)某個(gè)變量不需要時(shí),及時(shí)調(diào)用removeAttribute()方法清除變量。當(dāng)session終止時(shí)需要清除不必要的資源,實(shí)現(xiàn)HttpSessionBindingListener接口的valueUnbound()方法。
2) 使用include directive,而不使用include action;
目前在JSP頁(yè)面中引入外部資源的方法主要有兩種:include directive和include action。Include directive:例如<%@ include file=”copyright.html” %>,該指令在編譯時(shí)引入指定的資源。在編譯之前,帶有include指令的頁(yè)面和指定的資源被合并成一個(gè)文件。被引用的資源在編譯時(shí)就確定,比運(yùn)行時(shí)才確定資源更高效。Include action:例如< jsp:include page=”copyright.jsp” />,該動(dòng)作引入指定頁(yè)面執(zhí)行后生成的結(jié)果。由于它在運(yùn)行時(shí)完成,因此對(duì)輸出結(jié)果的控制更加靈活。但是,只有當(dāng)引用的內(nèi)容被頻繁改變時(shí),或者在對(duì)主頁(yè)面的請(qǐng)求沒(méi)有出現(xiàn)之前,被引用的頁(yè)面無(wú)法確定時(shí),使用include action才合算。
3) 對(duì)于那些無(wú)需跟蹤會(huì)話(huà)狀態(tài)的jsp,關(guān)閉自動(dòng)創(chuàng)建的會(huì)話(huà)可以節(jié)省一些資源。使用如下page指令: < %@ page session=”false” %>;
4) 盡量不要把jsp頁(yè)面定義為單線程,應(yīng)設(shè)置為< %@page isThreadSafe=”true” %>;
5) 在jsp頁(yè)面最好使用輸出緩存功能,如:< %@page buffer=”32kb” %>;
6) 在servlet之間跳轉(zhuǎn)時(shí),forward比sendRedirect更有效;
7) 設(shè)置HttpServletResponse緩沖區(qū),如:response.setBufferSize(20000);
8) 建議在servlet里使用ServletOutputStream輸出圖片等對(duì)象;
9) 不要使用SingleThreadModel,使Servlet是線程安全的,但是盡可能的減少消耗在同步代碼上的時(shí)間,使用足夠多的servlet去響應(yīng)用戶(hù)的請(qǐng)求;
10) 盡可能的使useBean的范圍在page范圍內(nèi);
11) Servlet的inti()和destroy()或jspInit()和jspDestroy()方法用于創(chuàng)建和刪除昂貴的資源,例如緩存對(duì)象和數(shù)據(jù)庫(kù)連接;
12) 避免使用反向DNS查找;
13) 預(yù)編譯JSP頁(yè)面;
14) 盡可能的在客戶(hù)段校驗(yàn)數(shù)據(jù);
15) 禁止自動(dòng)裝載特色防止周期性的裝載servlet和jsp。
由于EJB2.0已經(jīng)很少項(xiàng)目在用了,EJB3.0再成熟一點(diǎn),我再補(bǔ)充這一部分吧!