java學習-如何用Java進行高性能網站開發
1、生成對象時,合理分配空間和大小:
Java中的很多類都有它的默認的空間分配大小,對於一些有大小的對象的初始化,應該預計對象的大小,然後使用進行初始化。
例如:我們在使用Vector,當聲明Vector vect=new Vector()時,係統調用:
public Vector() {// 缺省構造函數
this(10); // 容量是 10;
}
缺省分配10個對象大小容量。當執行add方法時,可以看到具體實現為:..
public synchronized boolean add(Object o) {
modCount++;
ensureCapacityHelper(elementCount+1);
elementData[elementCount++] =o;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) :
(oldCapacity * 2);
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = new Object[newCapacity];
System.arraycopy(oldData, 0, elementData, 0, elementCount);
}
}
我們可以看到,當Vector大小超過原來的大小時,一些代碼的目的就是為了做容量的擴充,在預先知道該Vector大小的話,可以指定其大小,避免容量擴充的開銷。
如果想學習Java可以來這個群,首先是一二六,中間是五三四,最後是五一九,裏麵有大量的學習資料可以下載。
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()的係統調用開銷,避免了循環體重複調用。
再看如下的代碼片:..
for (int i = 0;i <100000;i++)
if (i%10 == 9) {
... // 每十次執行一次
}
改寫成也可以提高效率:..
for(inti =0,j =10; i<100000; i++,j--){
if(j == 0){
... // 每十次執行一次
j = 10;
}
}
所以,當有較大的循環時,應該檢查循環內是否有效率不高的地方,尋找更優的方案加以改進。
3、少用new初始化一個實例:
盡量少用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();
}
另外,應該盡量重複使用一個對象,而不是聲明新的同類對象。一個重用對象的方法是改變對象的值,如可以通過setValue之類的方法改變對象的變量達到重用的目的。
4、選擇合適的方法調用:
在Java中,一切都是對象,如果有方法(Method)調用,處理器先要檢查該方法是屬於哪個對象,該對象是否有效,對象屬於什麼類型,然後選擇合適的方法並調用。
可以減少方法的調用,同樣一個方法:
public void CallMethod(int i ){
if( i ==0 ){
return;
}
... // 其他處理
}
如果直接調用,
int i = 0;
...
CallMethod(i);
上麵的代碼,就應該寫成:
int i = 0;
...
if( i ==0 ){
CallMethod(i);
}
不影響可讀性等情況下,可以把幾個小的方法合成一個大的方法。
另外,在方法前加上final,private關鍵字有利於編譯器的優化。
5、異常處理技巧:
異常是Java的一種錯誤處理機製,對程序來說是非常有用的,但是異常對性能不利。拋出異常首先要創建一個新的對象,並進行相關的處理,造成係統的開銷,所以異常應該用在錯誤處理的情況,不應該用來控製程序流程,流程盡量用while,if等處理。
在不是很影響代碼健壯性的前提下,可以把幾個try/catch塊合成一個。
如果想學習Java可以來這個群,首先是一二六,中間是五三四,最後是五一九,裏麵有大量的學習資料可以下載。
6、盡量使用局部變量和靜態變量:
盡量使用局部變量,調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧(Stack) 中,速度較快。其他變量,如靜態變量、實例變量等,都在堆(Heap)中創建,速度較慢。
盡量使用靜態變量,即加修飾符static,如果類中的變量不會隨他的實例而變化,就可以定義為靜態變量,從而使他所有的實例都共享這個變量。
7、同步處理技巧:
同步主要出現在多線程的情況,為多線程同時運行時提供對象數據安全的機製,多線程是比較複雜話題,應用多線程也是為了獲得性能的提升,應該盡可能減少同步。
另外,如果需要同步的地方,可以減少同步的代碼段,如隻同步某個方法或函數,而不是整個代碼。
8、盡可能的使用Java自身提供的API:
Java的API一般都做了性能的考慮,如果完成相同的功能,優先使用API而不是自己寫的代碼,如數組複製通常的代碼如下:
int size = 1000;
String[] strArray1 = new String[size];
String[] strArray2 = new String[size];
for(inti=0;i<size;i++){ // 賦值
strArray1 = (new String("Array: " + i));
}
for(inti=0;i<size;i++){ // 複製
strArray2=(new String((String)a));
}
上麵那段代碼,如果使用Java提供的API,就可以提高性能:
int size = 1000;
String[] strArray1 = new String[size];
String[] strArray2 = new String[size];
for(inti=0;i<size;i++){ // 賦值
strArray1 = (new String("Array: " + i));
}
System.arraycopy(strArray1,0,strArray2,0,size); // 複製
同樣的一個規則是,當有大量數據的複製時,應該使用System.arraycopy()。
9、盡量減少I/O操作:
輸入/輸出(I/O)包括很多方麵,我們知道,進行I/O操作是很消耗係統資源的。程序中應該盡量少用I/O操作。使用時可以注意: . 合理控製輸出函數System.out.println()對於大多時候是有用的,特別是係統調試的時候,但也會產生大量的信息出現在控製台和日誌上,同時輸出時,有序列化和同步的過程,造成了開銷。
特別是在發行版中,要合理的控製輸出,可以在項目開發時,設計好一個Debug的工具類,在該類中可以實現輸出開關,輸出的級別,根據不同的情況進行不同的輸出的控製。
10、盡量使用緩存:
讀寫內存要比讀寫硬盤上的文件要快很多,應盡可能使用緩衝,以便直接從內存中讀取數據。
盡可能使用帶有Buffer的類代替沒有Buffer的類,如可以用BufferedReader 代替Reader,用BufferedWriter代替Writer來進行處理I/O操作。
同樣可以用BufferedInputStream代替InputStream都可以獲得性能的提高
11、盡量不使用同步:
Servlet是多線程的,以處理不同的請求,基於前麵同步的分析,如果有太多的同步就失去了多線程的優勢了。
12、不用保存太多的信息在HttpSession中
很多時候,存儲一些對象在HttpSession中是有必要的,可以加快係統的開發,如網上商店係統會把購物車信息保存在該用戶的Session中,但當存儲大量的信息或是大的對象在會話中時,是有害的,特別是當係統中用戶的訪問量很大,對內存的需求就會很高。
具體開發時,在這兩者之間應作好權衡。
13、清除SESSION
通常情況,當達到設定的超時時間時,同時有些Session沒有了活動,服務器會釋放這些沒有活動的Session,.. 不過這種情況下,特別是多用戶並訪時,係統內存要維護多個的無效Session。
當用戶退出時,應該手動釋放,回收資源,實現如下:..
HttpSession theSession = request.getSession();
// 獲取當前Session
if(theSession != null){
theSession.invalidate(); // 使該Session失效
}
14、緩存Home接口
EJB庫使用Enterprise Bean 的客戶端通過它的Home接口創建它的實例。客戶端能通過JNDI訪問它。服務器通過Lookup方法來獲取。
JNDI是個遠程對象,通過RMI方式調用,對它的訪問往往是比較費時的。所以,在設計時可以設計一個類專門用來緩存Home接口,在係統初始化時就獲得需要的Home接口並緩存,以後的引用隻要引用緩存即可。
15、使用快速度的Jdbc驅動
JDBC API包括兩種實現接口形式,一種是純Java實現的驅動,一種利用ODBC驅動和數據庫客戶端實現,具體有四種驅動模式:
第一類:JDBC-ODBC橋,再加上ODBC驅動程序。
第一類JDBC驅動程序是JDBC-ODBC橋再加上一個ODBC驅動程序。建議第一類驅動程序隻用於原型開發,而不要用於正式的運行環境。橋接驅動程序由Sun提供,它的目標是支持傳統的數據庫係統。Sun為該軟件提供關鍵問題的補丁,但不為該軟件的最終用戶提供支持。一般地,橋接驅動程序用於已經在ODBC技術上投資的情形,例如已經投資了Windows應用服務器。
盡管Sun提供了JDBC-ODBC橋接驅動程序,但由於ODBC會在客戶端裝載二進製代碼和數據庫客戶端代碼,這種技術不適用於高事務性的環境。另外,第一類JDBC驅動程序不支持完整的Java命令集,而是局限於ODBC驅動程序的功能,這種驅動方式也叫胖客戶,主要用於低並發請求,大數據量傳輸的應用。
第二類:本機API,部分是Java的驅動程序。
第二類JDBC驅動程序是本機API的部分Java代碼的驅動程序,用於把JDBC調用轉換成主流數據庫API的本機調用。這類驅動程序也存在與第一類驅動程序一樣的性能問題,即客戶端載入二進製代碼的問題,而且它們被綁定了特定的平台。
第二類驅動程序要求編寫麵向特定平台的代碼,主流的數據庫廠商,例如Oracle和IBM,都為它們的企業數據庫平台提供了第二類驅動程序,使用這些驅動程序的開發者必須及時跟進不同數據庫廠商針對不同操作係統發行的各個驅動程序版本。
另外,由於第二類驅動程序沒有使用純Java的API,把Java應用連接到數據源時,往往必須執行一些額外的配置工作。很多時候,第二類驅動程序不能在體係結構上與大型主機的數據源兼容;即使做到了兼容,效果也是比較差。
第一類和第二類驅動的比較
第一類和第二類驅動的比較
第三類:麵向數據庫中間件的純Java驅動程序
第三類JDBC驅動程序是麵向數據庫中間件的純Java驅動程序,JDBC調用被轉換成一種中間件廠商的協議,中間件再把這些調用轉換到數據庫API。第三類JDBC驅動程序的優點是它以服務器為基礎,也就是不再需要客戶端的本機代碼,這使第三類驅動程序要比第一、二兩類快。另外,開發者還可以利用單一的驅動程序連接到多種數據庫。
第四類:直接麵向數據庫的純Java驅動程序。
第四類JDBC驅動程序是直接麵向數據庫的純Java驅動程序,即所謂的“瘦”(thin)驅動程序,它把JDBC調用轉換成某種直接可被DBMS使用的網絡協議,這樣,客戶機和應用服務器可以直接調用DBMS服務器。對於第四類驅動程序,不同DBMS的驅動程序不同。因此,在一個異構計算環境中,驅動程序的數量可能會比較多。但是,由於第四類驅動程序具有較高的性能,能夠直接訪問DBMS,所以這一問題就不那麼突出了, 這種驅動方式,主要用於高並發,低數據量請求的應用中。
第三類和第四類驅動的比較
第三類和第四類驅動的比較
為了提高訪問數據庫的性能,我們還可以使用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");
16、緩存DataSorce
一個DataSource對象代表一個實際的數據源。這個數據源可以是從關係數據庫到表格形式的文件,完全依賴於它是怎樣實現的,一個數據源對象注冊到JNDI名字服務後,應用程序就可以從JNDI服務器上取得該對象,並使用之和數據源建立連接。
通過上麵的例子,我們知道DataSource是從連接池獲得連接的一種方式,通過JNDI方式獲得,是占用資源的。
為了避免再次的JNDI調用,可以係統中緩存要使用的DataSource。
及時關閉使用過的資源
互聯網應用係統一般是並發的係統,在每次申請和使用完資源後,應該釋放供別人使用,使用完成後應該保證徹底的釋放。
17、架構選型
在網站Web應用開發中,係統的整體架構是決定網站性能、穩定性、並發、可擴展性的關鍵因素。下麵以世界著名網站應用軟件提供商CoreMediaCMS係統為例,進行架構分析
CoreMediaCMS將整個應用分成四成架構,每一層都可以獨立於其他層而正常運行,每一層都可以分布式布署,極大的提高了應用係統的穩定性、可擴展性、支持高並發的要求,每一次之前通過中間件Corba進行穩定的傳輸數據。
18、開發框架的選型
充分利用開源框架,可以大大提高開發效率。很多初級開發者,都采用DB+JavaBean+JSP這種初級的開發模式,而現在主要使用Struts、Spring等MVC開發框架。
常用開發框架構選型有:
Struts、Spring、Webwork等。
我們選擇的開發框架是:Spring+Hibernate+Spring MVC,在這個開發框架裏,充分利用了Hibernate、Spring各自己的優點,可以選擇Struts,也可以選擇Spring MVC。
如果想學習Java可以來這個群,首先是一二六,中間是五三四,最後是五一九,裏麵有大量的學習資料可以下載。
19、分級存儲
1)數據庫數據分級存儲:
將經常訪問的數據和訪問頻度低的數據,分別存放到不同的分區,甚至存放到不同的數據庫服務器,以便合進分配硬盤I/O及係統I/O。
2)網站內容發布之後,分級存儲:
任何一個大型的網站,一般都有海量的內容,為了提高訪問效率,應搭建分級存儲體係,根據應用的重要性和訪問並發要求,將這些內容分級存儲,同時將靜態內容中的靜態頁麵文件、圖片文件、下載文件分不同的Web服務器訪問,降低I/O爭用,提高訪問效率,同時讓數據存儲、管理、備份更加清晰。
20、頁麵靜態化
一個大型網站,既有靜態內容,也有動態內容。靜態內容,直接通過Apache或者Squid訪問,效率高,穩定可靠,更多的是受服務器等硬件設備的I/O吞吐量、網絡環境及頁麵代碼本身質量限製,不受應用係統及數據庫性能限製,這些內容往往訪問速度和效率不會有較大的問題。
而動態內容,除了受硬件設備I/O、操作係統I/O及內容、網絡環境及頁麵代碼的影響,還要受應用服務器和數據庫性能影響,因此,這部份內容,要盡可能作靜態化或者偽靜態,並采用緩存技術,將其緩存,以減少對應用服務器和數據庫服務器的操作次數,提高用戶訪問效率和穩定性。
21、緩存策略
對於構建的業務係統,如果有些數據要經常要從數據庫中讀取,同時,這些數據又不經常變化,這些數據就可以在係統中緩存起來,使用時直接讀取緩存,而不用頻繁的訪問數據庫讀取數據。
緩存工作可以在係統初始化時一次性讀取數據,特別是一些隻讀的數據,當數據更新時更新數據庫內容,同時更新緩存的數據值。
例如:在天極CMS2005係統中,我們將很少發生變化的網站節點樹數據,緩存在客戶端,當用戶登錄時,一次性讀入到客戶端緩存起來,以後編輯在使用時,不用再從數據庫中讀取,大大提高了應用係統的訪問速度。
當然,也可以將數據庫中重複訪問的數據緩存在應用服務器內存中,減少對數據庫的訪問次數,Java常用的緩存技術產品有:MemoryCache、OSCache等。
最後更新:2017-04-15 20:02:44