J2EE中的優化方案
1.使用StringBuffer代替String
String是用來存儲字符串常量的,如果要執行“+”的操作,系統會生成一些臨時的對象,并對這些對象進行管理,造成不必要的開銷。
如果字符串有連接的操作,替代的做法是用StringBuffer類的append方法。
當字符串的大小超過缺省16時,代碼實現了容量的擴充,為了避免對象的重新擴展其容量,那么最好用StringBuffer sb=new StringBuffer(30)
2.優化循環體
Vector vect = new Vector(1000);
for( inti=0; i<vect.size(); i++){
...
}
for循環部分改寫成:
int size = vect.size();
for( int i=0; i>size; i++){
...
}
如果size=1000,就避免了1000次的size()調用開銷,避免了重復調用.
3.對象的創建
盡量少用new來初始化一個類的實例,當一個對象是用new進行初始化時,其構造函數鏈的所有構造函數都被調用到,所以new操作符是很消耗系統資源的,new一個對象耗時往往是局部變量賦值耗時的上千倍。同時,當生成對象后,系統還要花時間進行垃圾回收和處理。
當new創建對象不可避免時,注意避免多次的使用new初始化一個對象。
盡量在使用時再創建該對象。
NewObject object = new NewObject();
int value;
if(i>0 )
{
value =object.getValue();
}
可以修改為:
int value;
if(i>0 )
{
NewObject object = new NewObject();
Value =object.getValue();
}
4.慎用異常處理
異常是Java的一種錯誤處理機制,對程序來說是非常有用的,但是異常對性能不利。拋出異常首先要創建一個新的對象,并進行相關的處理,造成系統的開銷,所以異常應該用在錯誤處理的情況,不應該用來控制程序流程,流程盡量用while,if等處理。
在不是很影響代碼健壯性的前提下,可以把幾個try/catch塊合成一個。
討論:需要處理的異常處理和不需要處理的異常處理在捕捉異常類型上應該采取什么策略。
5.變量的使用
盡量使用局部變量,調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧(Stack) 中,速度較快。其他變量,如靜態變量、實例變量等,都在堆(Heap)中創建,速度較慢。
盡量使用靜態變量,即加修飾符static,如果類中的變量不會隨他的實例而變化,就可以定義為靜態變量,從而使他所有的實例都共享這個變量。
(但是靜態變量不能濫用,如果這個變量用的比較少而聲明成靜態變量,不僅效率不會提高,還會影響性能)
6.I/O操作
輸入/輸出(I/O)包括很多方面,我們知道,進行I/O操作是很費系統資源的。程序中應該盡量少用I/O操作。使用時可以注意: . 合理控制輸出函數System.out.println()對于大多時候是有用的,特別是系統調試的時候,但也會產生大量的信息出現在控制臺和日志上,同時輸出時,有序列化和同步的過程,造成了開銷。
特別是在發行版中,要合理的控制輸出,可以在項目開發時,設計好一個Debug的工具類,在該類中可以實現輸出開關,輸出的級別,根據不同的情況進行不同的輸出的控制。
我們在調試程序的時候,我們喜歡用System.out.println()這樣的打印語句來跟蹤我們的錯誤。但在程序發布的時候我們卻沒有把這樣的語句都刪除掉,在項目的運行中,也會影響效能。
7.使用緩存
讀寫內存要比讀寫文件要快很多,應盡可能使用緩沖。
盡可能使用帶有Buffer的類代替沒有Buffer的類,如可以用BufferedReader 代替Reader,用BufferedWriter代替Writer來進行處理I/O操作。
同樣可以用BufferedInputStream代替InputStream都可以獲得性能的提高。
8.Servlet的效率問題
Servlet采用請求——響應模式提供Web服務,通過ServletResponse以及ServletRequest這兩個對象來輸出和接收用戶傳遞的參數,在服務器端處理用戶的請求,根據請求訪問數據庫、訪問別的Servlet方法、調用EJB等等,然后將處理結果返回給客戶端。
·盡量不使用同步
Servlet是多線程的,以處理不同的請求,基于前面同步的分析,如果有太多的同步就失去了多線程的優勢了。
同步大多數使用在一個完整的事務中,避免事務中多方操作而引起的數據不同步現象。能用邏輯控制的盡量的用邏輯控制。
·不用保存太多的信息在HttpSession中
很多時候,存儲一些對象在HttpSession中是有必要的,可以加快系統的開發,如網上商店系統會把購物車信息保存在該用戶的Session中,但當存儲大量的信息或是大的對象在會話中是有害的,特別是當系統中用戶的訪問量很大,對內存的需求就會很高。
具體開發時,在這兩者之間應作好權衡。
我們在寫web應用時,經常只需要request的地方而使用了session,這樣會是效率大大的降低的。但是我們用到了session,在項目的開發中又不能很好的利用session,這樣對效率都是有影響的。比如說,我們把用戶信息放在了session中,但是我們有時候在判斷用戶是否登錄的時候,我們傾向去數據庫里查這個是否有這個用戶,又是用了request,但是這樣是沒有必要的,因為我們只需要判斷session就行了。如果并發數量比較大的時候,服務器的負載就會過重。
·清除Session
通常情況,當達到設定的超時時間時,同時有些Session沒有了活動,服務器會釋放這些沒有活動的Session,.. 不過這種情況下,特別是多用戶并訪時,系統內存要維護多個的無效Session。
當用戶退出時,應該手動釋放,回收資源,實現如下:..
HttpSession theSession = request.getSession();
// 獲取當前Session
if(theSession != null){
theSession.invalidate(); // 使該Session失效
}
9.數據庫的操作
在J2EE開發的應用系統中,數據庫訪問一般是個必備的環節。數據庫用來存儲業務數據,供應用程序訪問。
在Java技術的應用體系中,應用程序是通過JDBC(Java Database Connectivity)實現的接口來訪問數據庫的,JDBC支持“建立連接、SQL語句查詢、處理結果”等基本功能。在應用JDBC接口訪問數據庫的過程中,只要根據規范來實現,就可以達到要求的功能。
但是,有些時候進行數據查詢的效率著實讓開發人員不如所愿,明明根據規范編寫的程序,運行效果卻很差,造成整個系統的執行效率不高。
·使用速度快的JDBC驅動
JDBC API包括兩種實現接口形式,一種是純Java實現的驅動,一種利用ODBC驅動和數據庫客戶端實現,具體有四種驅動模式并各有不同的應用范圍,針對不同的應用開發要選擇合適的JDBC驅動,在同一個應用系統中,如果選擇不同的JDBC驅動,在效率上會有差別。
例如,有一個企業應用系統,不要求支持不同廠商的數據庫,這時就可以選擇模式4的JDBC驅動,該驅動一般由數據庫廠商實現的基于本地協議的驅動,直接調用數據庫管理系統使用的協議,減少了模式3中的中間層。
·使用JDBC連接池
為了提高訪問數據庫的性能,我們還可以使用JDBC 2.0的一些規范和特性,JDBC是占用資源的,在使用數據庫連接時可以使用連接池Connection Pooling,避免頻繁打開、關閉Connection。而我們知道,獲取Connection是比較消耗系統資源的。
Connection緩沖池是這樣工作的:當一個應用程序關閉一個數據庫連接時,這個連接并不真正釋放而是被循環利用,建立連接是消耗較大的操作,循環利用連接可以顯著的提高性能,因為可以減少新連接的建立。
一個通過DataSource獲取緩沖池獲得連接,并連接到一個CustomerDB數據源的代碼演示如下:
Context ctx = new InitialContext();
DataSource dataSource = (DataSource) ctx.lookup("jdbc/CustomerDB");
Connection conn = dataSource.getConnection("password","username");
·緩存DataSource
一個DataSource對象代表一個實際的數據源。這個數據源可以是從關系數據庫到表格形式的文件,完全依賴于它是怎樣實現的,一個數據源對象注冊到JNDI名字服務后,應用程序就可以從JNDI服務器上取得該對象,并使用之和數據源建立連接。
通過上面的例子,我們知道DataSource是從連接池獲得連接的一種方式,通過JNDI方式獲得,是占用資源的。
為了避免再次的JNDI調用,可以系統中緩存要使用的DataSource。
·關閉所有使用的資源
系統一般是并發的系統,在每次申請和使用完資源后,應該釋放供別人使用,數據庫資源每個模式的含義可以參考SUN JDBC的文檔,不同是比較寶貴的,使用完成后應該保證徹底的釋放。
請看下面的代碼段:
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
DataSource dataSource = getDataSource();
// 取的DataSource的方法,實現略。
conn = datasource.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM ...");
... // 其他處理
rs.close();
stmt.close();
conn.close();
}catch (SQLException ex) {
... // 錯誤處理
}
粗看似乎沒有什么問題,也有關閉相關如Connection等系統資源的代碼,但當出現異常后,關閉資源的代碼可能并不被執行,為保證資源的確實已被關閉,應該把資源關閉的代碼放到finally塊:
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
DataSource dataSource = getDataSource();
// 取的DataSource的方法,實現略。
conn = datasource.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM ...");
... // 其他處理
}catch (SQLException ex) {
... // 錯誤處理
}finally{
if (rs!=null) {
try {
rs.close(); // 關閉ResultSet}
catch (SQLException ex) {
... // 錯誤處理
}
}
if (stmt!=null){
try {
stmt.close(); // 關閉Statement}
catch (SQLException ex) {
... // 錯誤處理
}
}
if (conn!=null){
try {
conn.close(); // 關閉Connection}
catch (SQLException ex) {
... // 錯誤處理
}
}
}
·大型數據量處理
當我們在讀取諸如數據列表、報表等大量數據時,可以發現使用EJB的方法是非常慢的,這時可以使用直接訪問數據庫的方法,用SQL直接存取數據,從而消除EJB的經常開支(例如遠程方法調用、事務管理和數據序列化,對象的構造等)。
·緩存經常使用的數據
對于構建的業務系統,如果有些數據要經常要從數據庫中讀取,同時,這些數據又不經常變化,這些數據就可以在系統中緩存起來,使用時直接讀取緩存,而不用頻繁的訪問數據庫讀取數據。
緩存工作可以在系統初始化時一次性讀取數據,特別是一些只讀的數據,當數據更新時更新數據庫內容,同時更新緩存的數據值。
一個例子是,在一套企業應用系統中,企業的信息數據(如企業的名稱)在多個業務應用模塊中使用,這時就可以把這些數據緩存起來,需要時直接讀取緩存的企業信息數據。
我們經常使用Hibernate這樣的持久數據層的框架,就很好的利用了緩存的作用,所以在一定程度上彌補了封裝而帶來的效率丟失問題。但是在使用緩存的時候應該注意,因為訪問的內存數據而不是真實的數據庫數據,所以會出現臟讀的情況。同時也并不是所有的東西都應該放在緩存中,不經常用到的我們就沒有必要放在緩存中。有時候我們配合xml文件來使用,也不失一種好的方法。
一般意義上說,參與系統運行的代碼都會對性能產生影響,實際應用中應該養成良好的編程規范、編寫高質量的代碼,當系統性能出現問題時,要找到主要影響性能的瓶頸所在,然后集中精力優化這些代碼,能達到事半功倍的效果。
J2EE性能的優化包括很多方面的,要達到一個性能優良的系統,除了關注代碼之外,還應該根據系統實際的運行情況,從服務器軟硬件環境、集群技術、系統構架設計、系統部署環境、數據結構、算法設計等方面綜合考慮。
本Blog純屬個人學習、工作需要,記錄相關資料。請不要發表任何有人身攻擊的言論,謝謝! www.zhipsoft.cn
posted on 2007-06-12 08:49
ZhipSoft 閱讀(606)
評論(0) 編輯 收藏 所屬分類:
Java