同樣的一個規則是,當有大量數據的復制時,應該使用System.arraycopy()。
三、I/O 性能
輸入/輸出(I/O)包括很多方面,我們知道,進行I/O操作是很費系統資源的。程序中應該盡量少用I/O操作。使用時可以注意: . 合理控制輸出函數System.out.println()對於大多時候是有用的,特別是系統調試的時候,但也會產生大量的信息出現在控制台和日志上,同時輸出時,有序列化和同步的過程,造成了開銷。
特別是在發行版中,要合理的控制輸出,可以在項目開發時,設計好一個Debug的工具類,在該類中可以實現輸出開關,輸出的級別,根據不同的情況進行不同的輸出的控制。
·使用緩存
讀寫內存要比讀寫文件要快很多,應盡可能使用緩沖。
盡可能使用帶有Buffer的類代替沒有Buffer的類,如可以用BufferedReader 代替Reader,用BufferedWriter代替Writer來進行處理I/O操作。
同樣可以用BufferedInputStream代替InputStream都可以獲得性能的提高。
四、Servlet
Servlet采用請求——響應模式提供Web服務,通過ServletResponse以及ServletRequest這兩個對象來輸出和接收用戶傳遞的參數,在服務器端處理用戶的請求,根據請求訪問數據庫、訪問別的Servlet方法、調用EJB等等,然後將處理結果返回給客戶端。
·盡量不使用同步
Servlet是多線程的,以處理不同的請求,基於前面同步的分析,如果有太多的同步就失去了多線程的優勢了。
·不用保存太多的信息在HttpSession中
很多時候,存儲一些對象在HttpSession中是有必要的,可以加快系統的開發,如網上商店系統會把購物車信息保存在該用戶的Session中,但當存儲大量的信息或是大的對象在會話中是有害的,特別是當系統中用戶的訪問量很大,對內存的需求就會很高。
具體開發時,在這兩者之間應作好權衡。
·清除Session
通常情況,當達到設定的超時時間時,同時有些Session沒有了活動,服務器會釋放這些沒有活動的Session,.. 不過這種情況下,特別是多用戶並訪時,系統內存要維護多個的無效Session。
當用戶退出時,應該手動釋放,回收資源,實現如下:..
HttpSession theSession = request.getSession();
// 獲取當前Session
if(theSession != null){
theSession.invalidate(); // 使該Session失效
}
五、EJB 問題
EJB是Java服務器端服務框架的規范,軟件廠商根據它來實現EJB服務器。應用程序開發者可以專注於支持應用所需的商業邏輯,而不用擔心周圍框架的實現問題。EJB規范詳細地解釋了一些最小但是必須的服務,如事務,安全和名字等。
·緩存Home接口
EJB庫使用Enterprise Bean 的客戶端通過它的Home接口創建它的實例。客戶端能通過JNDI訪問它。服務器通過Lookup方法來獲取。
JNDI是個遠程對象,通過RMI方式調用,對它的訪問往往是比較費時的。所以,在設計時可以設計一個類專門用來緩存Home接口,在系統初始化時就獲得需要的Home接口並緩存,以後的引用只要引用緩存即可。
·封裝Entity Bean
直接訪問Entity Bean是個不好的習慣,用會話Bean封裝對實體Bean的訪問能夠改進事務管理,因為每一個對get方法的直接調用將產生一個事務,容器將在每一個實體Bean的事務之後執行一個“Load-Store”.. 操作。
最好在Session Bean中完成Entity Bean的封裝,減少容器的事務處理,並在Session Bean中實現一些具體的業務方法。
·釋放有狀態的Session Bean
相當於HttpSession,當把一個Session Bean設為Stateful,即有狀態的Session Bean 後,應用容器(Container)就可能有“鈍化”(Passivate)和活化(Activate)過程,即在主存和二級緩存之間對SessionBean進行存儲位置的轉移,在這個過程中,存在序列化過程。
通常有狀態Session Bean的釋放是在超時時發生,容器自動的清除該對象,但是如果交給容器管理,一方面可能產生對象鈍化,另一方面未超時期間,系統還要 維護一份該對象,所以如果我們確認使用完該StatefulSession Bean後不再需要時,可以顯式的將其釋放掉,方法是調用:
theSesionBean.remove();
六、數據庫訪問
在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的經常開支(例如遠程方法調用、事務管理和數據序列化,對象的構造等)。
·緩存經常使用的數據
對於構建的業務系統,如果有些數據要經常要從數據庫中讀取,同時,這些數據又不經常變化,這些數據就可以在系統中緩存起來,使用時直接讀取緩存,而不用頻繁的訪問數據庫讀取數據。
緩存工作可以在系統初始化時一次性讀取數據,特別是一些只讀的數據,當數據更新時更新數據庫內容,同時更新緩存的數據值。
一個例子是,在一套企業應用系統中,企業的信息數據(如企業的名稱)在多個業務應用模塊中使用,這時就可以把這些數據緩存起來,需要時直接讀取緩存的企業信息數據。
七、總結
一般意義上說,參與系統運行的代碼都會對性能產生影響,實際應用中應該養成良好的編程規范、編寫高質量的代碼,當系統性能出現問題時,要找到主要影響性能的瓶頸所在,然後集中精力優化這些代碼,能達到事半功倍的效果。
J2EE性能的優化包括很多方面的,要達到一個性能優良的系統,除了關注代碼之外,還應該根據系統實際的運行情況,從服務器軟硬件環境、集群技術、系統構架設計、系統部署環境、數據結構、算法設計等方面綜合考慮。