通過使用一些輔助性工具來找到程序中的瓶頸然後就可以對瓶頸部分的代碼進行優化一般有兩種方案即優化代碼或更改設計方法我們一般會選擇後者因為不去調用以下代碼要比調用一些優化的代碼更能提高程序的性能而一個設計良好的程序能夠精簡代碼從而提高性能
下面將提供一些在JAVA程序的設計和編碼中為了能夠提高JAVA程序的性能而經常采用的一些方法和技巧
.對象的生成和大小的調整
JAVA程序設計中一個普遍的問題就是沒有好好的利用JAVA語言本身提供的函數從而常常會生成大量的對象(或實例)由於系統不僅要花時間生成對象以後可能還需花時間對這些對象進行垃圾回收和處理因此生成過多的對象將會給程序的性能帶來很大的影響
例關於String StringBuffer+和append
JAVA語言提供了對於String類型變量的操作但如果使用不當會給程序的性能帶來影響如下面的語句
String name=new String(HuangWeiFeng);
Systemoutprintln(name+is my name);
看似已經很精簡了其實並非如此為了生成二進制的代碼要進行如下的步驟和操作
() 生成新的字符串 new String(STR_);
() 復制該字符串;
() 加載字符串常量HuangWeiFeng(STR_);
() 調用字符串的構架器(Constructor);
() 保存該字符串到數組中(從位置開始);
() 從javaioPrintStream類中得到靜態的out變量;
() 生成新的字符串緩沖變量new StringBuffer(STR_BUF_);
() 復制該字符串緩沖變量;
() 調用字符串緩沖的構架器(Constructor);
() 保存該字符串緩沖到數組中(從位置開始);
() 以STR_為參數調用字符串緩沖(StringBuffer)類中的append方法;
() 加載字符串常量is my name(STR_);
() 以STR_為參數調用字符串緩沖(StringBuffer)類中的append方法;
() 對於STR_BUF_執行toString命令;
() 調用out變量中的println方法輸出結果
由此可以看出這兩行簡單的代碼就生成了STR_STR_STR_STR_和STR_BUF_五個對象變量這些生成的類的實例一般都存放在堆中堆要對所有類的超類類的實例進行初始化同時還要調用類極其每個超類的構架器而這些操作都是非常消耗系統資源的因此對對象的生成進行限制是完全有必要的
經修改上面的代碼可以用如下的代碼來替換
StringBuffer name=new StringBuffer(HuangWeiFeng);
Systemoutprintln(nameappend(is my name)toString());
系統將進行如下的操作
() 生成新的字符串緩沖變量new StringBuffer(STR_BUF_);
() 復制該字符串緩沖變量;
() 加載字符串常量HuangWeiFeng(STR_);
() 調用字符串緩沖的構架器(Constructor);
() 保存該字符串緩沖到數組中(從位置開始);
() 從javaioPrintStream類中得到靜態的out變量;
() 加載STR_BUF_;
() 加載字符串常量is my name(STR_);
() 以STR_為參數調用字符串緩沖(StringBuffer)實例中的append方法;
() 對於STR_BUF_執行toString命令(STR_);
()調用out變量中的println方法輸出結果
由此可以看出經過改進後的代碼只生成了四個對象變量STR_STR_STR_和STR_BUF_你可能覺得少生成一個對象不會對程序的性能有很大的提高但下面的代碼段的執行速度將是代碼段的倍因為代碼段生成了八個對象而代碼段只生成了四個對象
代碼段
String name= new StringBuffer(HuangWeiFeng);
name+=is my;
name+=name;
代碼段
StringBuffer name=new StringBuffer(HuangWeiFeng);
nameappend(is my);
nameappend(name)toString();
因此充分的利用JAVA提供的庫函數來優化程序對提高JAVA程序的性能時非常重要的其注意點主要有如下幾方面
() 盡可能的使用靜態變量(Static Class Variables)
如果類中的變量不會隨他的實例而變化就可以定義為靜態變量從而使他所有的實例都共享這個變量
例
public class foo
{
SomeObject so=new SomeObject();
}
就可以定義為
public class foo
{
static SomeObject so=new SomeObject();
}
() 不要對已生成的對象作過多的改變
對於一些類(如String類)來講寧願在重新生成一個新的對象實例而不應該修改已經生成的對象實例
例
String name=Huang;
name=Wei;
name=Feng;
上述代碼生成了三個String類型的對象實例而前兩個馬上就需要系統進行垃圾回收處理如果要對字符串進行連接的操作性能將得更差因為系統將不得為此生成更多得臨時變量如上例所示
() 生成對象時要分配給它合理的空間和大小JAVA中的很多類都有它的默認的空間分配大小對於StringBuffer類來講默認的分配空間大小是個字符如果在程序中使用StringBuffer的空間大小不是個字符那麼就必須進行正確的初始化
() 避免生成不太使用或生命周期短的對象或變量對於這種情況因該定義一個對象緩沖池以為管理一個對象緩沖池的開銷要比頻繁的生成和回收對象的開銷小的多
() 只在對象作用范圍內進行初始化JAVA允許在代碼的任何地方定義和初始化對象這樣就可以只在對象作用的范圍內進行初始化從而節約系統的開銷
例
SomeObject so=new SomeObject();
If(x==) then
{
Foo=sogetXX();
}
可以修改為
if(x==) then
{
SomeObject so=new SomeObject();
Foo=sogetXX();
}
.異常(Exceptions)
JAVA語言中提供了try/catch來發方便用戶捕捉異常進行異常的處理但是如果使用不當也會給JAVA程序的性能帶來影響因此要注意以下兩點
() 避免對應用程序的邏輯使用try/catch
如果可以用ifwhile等邏輯語句來處理那麼就盡可能的不用try/catch語句
() 重用異常
在必須要進行異常的處理時要盡可能的重用已經存在的異常對象以為在異常的處理中生成一個異常對象要消耗掉大部分的時間
線程(Threading)
一個高性能的應用程序中一般都會用到線程因為線程能充分利用系統的資源在其他線程因為等待硬盤或網絡讀寫而 時程序能繼續處理和運行但是對線程運用不當也會影響程序的性能
例正確使用Vector類
Vector主要用來保存各種類型的對象(包括相同類型和不同類型的對象)但是在一些情況下使用會給程序帶來性能上的影響這主要是由Vector類的兩個特點所決定的第一Vector提供了線程的安全保護功能即使Vector類中的許多方法同步但是如果你已經確認你的應用程序是單線程這些方法的同步就完全不必要了第二在Vector查找存儲的各種對象時常常要花很多的時間進行類型的匹配而當這些對象都是同一類型時這些匹配就完全不必要了因此有必要設計一個單線程的保存特定類型對象的類或集合來替代Vector類用來替換的程序如下(StringVectorjava)
public class StringVector
{
private String [] data;
private int count;
public StringVector()
{
this(); // default size is
}
public StringVector(int initialSize)
{
data = new String[initialSize];
}
public void add(String str)
{
// ignore null strings
if(str == null) { return; }
ensureCapacity(count + );
data[count++] = str;
}
private void ensureCapacity(int minCapacity)
{
int oldCapacity = datalength;
if (minCapacity > oldCapacity)
{
String oldData[] = data;
int newCapacity = oldCapacity * ;
data = new String[newCapacity];
Systemarraycopy(oldData data count);
}
}
public void remove(String str)
{
if(str == null) { return; // ignore null str }
for(int i = ; i < count; i++)
{
// check for a match
if(data[i]equals(str))
{
Systemarraycopy(datai+dataicount); // copy data
// allow previously valid array element be gc?d
data[count] = null;
return;
}
}
}
public final String getStringAt(int index)
{
if(index < ) { return null; }
else if(index > count) { return null; // index is > # strings }
else { return data[index]; // index is good }
}
}
因此代碼
Vector Strings=new Vector();
Stringsadd(One);
Stringsadd(Two);
String Second=(String)StringselementAt();
可以用如下的代碼替換
StringVector Strings=new StringVector();
Stringsadd(One);
Stringsadd(Two);
String Second=StringsgetStringAt();
這樣就可以通過優化線程來提高JAVA程序的性能用於測試的程序如下(TestCollectionjava):
import javautilVector;
public class TestCollection
{
public static void main(String args [])
{
TestCollection collect = new TestCollection();
if(argslength == )
{
Systemoutprintln(Usage: java TestCollection [ vector | stringvector ]);
Systemexit();
}
if(args[]equals(vector))
{
Vector store = new Vector();
long start = SystemcurrentTimeMillis();
for(int i = ; i < ; i++)
{
storeaddElement(string);
}
long finish = SystemcurrentTimeMillis();
Systemoutprintln((finishstart));
start = SystemcurrentTimeMillis();
for(int i = ; i < ; i++)
{
String result = (String)storeelementAt(i);
}
finish = SystemcurrentTimeMillis();
Systemoutprintln((finishstart));
}
else if(args[]equals(stringvector))
{
StringVector store = new StringVector();
long start = SystemcurrentTimeMillis();
for(int i = ; i < ; i++) { storeadd(string); }
long finish = SystemcurrentTimeMillis();
Systemoutprintln((finishstart));
start = SystemcurrentTimeMillis();
for(int i = ; i < ; i++) {
String result = storegetStringAt(i);
}
finish = SystemcurrentTimeMillis();
Systemoutprintln((finishstart));
}
}
}
關於線程的操作要注意如下幾個方面
() 防止過多的同步
如上所示不必要的同步常常會造成程序性能的下降因此如果程序是單線程則一定不要使用同步
() 同步方法而不要同步整個代碼段
對某個方法或函數進行同步比對整個代碼段進行同步的性能要好
() 對每個對象使用多鎖的機制來增大並發
一般每個對象都只有一個鎖這就表明如果兩個線程執行一個對象的兩個不同的同步方法時會發生死鎖即使這兩個方法並不共享任何資源為了避免這個問題可以對一個對象實行多鎖的機制如下所示
class foo
{
private static int var;
private static Object lock=new Object();
private static int var;
private static Object lock=new Object();
public static void increment()
{
synchronized(lock)
{
var++;
}
}
public static void increment()
{
synchronized(lock)
{
var++;
}
}
}
.輸入和輸出(I/O)
輸入和輸出包括很多方面但涉及最多的是對硬盤網絡或數據庫的讀寫操作對於讀寫操作又分為有緩存和沒有緩存的對於數據庫的操作又可以有多種類型的JDBC驅動器可以選擇但無論怎樣都會給程序的性能帶來影響因此需要注意如下幾點
() 使用輸入輸出緩沖
盡可能的多使用緩存但如果要經常對緩存進行刷新(flush)則建議不要使用緩存
() 輸出流(Output Stream)和Unicode字符串
當時用Output Stream和Unicode字符串時Write類的開銷比較大因為它要實現Unicode到字節(byte)的轉換因此如果可能的話在使用Write類之前就實現轉換或用OutputStream類代替Writer類來使用
() 當需序列化時使用transient
當序列化一個類或對象時對於那些原子類型(atomic)或可以重建的原素要表識為transient類型這樣就不用每一次都進行序列化如果這些序列化的對象要在網絡上傳輸這一小小的改變對性能會有很大的提高
() 使用高速緩存(Cache)
對於那些經常要使用而又不大變化的對象或數據可以把它存儲在高速緩存中這樣就可以提高訪問的速度這一點對於從數據庫中返回的結果集尤其重要
() 使用速度快的JDBC驅動器(Driver)
JAVA對訪問數據庫提供了四種方法這其中有兩種是JDBC驅動器一種是用JAVA外包的本地驅動器另一種是完全的JAVA驅動器具體要使用哪一種得根據JAVA布署的環境和應用程序本身來定
一些其他的經驗和技巧
() 使用局部變量
() 避免在同一個類中動過調用函數或方法(get或set)來設置或調用變量
() 避免在循環中生成同一個變量或調用同一個函數(參數變量也一樣)
() 盡可能的使用staticfinalprivate等關鍵字
() 當復制大量數據時使用Systemarraycopy()命令
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26257.html