整理了一些影響效能的代碼和最佳化方法,以後希望能陸續補充和最佳化
1. 如何使用Exception
Exception降低效能。一個異常拋出首先需要建立一個新的對象。Throwable介面中的構造器調用名為fillInStackTrace()的本地方法。這個方法負責巡檢棧的整個架構來收集跟蹤資訊。這樣無論何時有異常拋出,它要求虛擬機器裝載調用棧,因為一個新的對象在中部被建立。
異常應當僅用於有錯誤發生時,而不要控制流程。
2. 不要兩次初始設定變數
Java通過調用獨特的類構造器預設地初始設定變數為一個已知的值。所有的對象被設定成null,integers (byte, short, int, long)被設定成0,float和double設定成0.0,Boolean變數設定成false。這對那些擴充自其它類的類尤其重要,這跟使用一個新的關鍵詞建立一個對象時所有一連串的構造器被自動調用一樣。
3. 在任何可能的地方讓類為Final
標記為final的類不能被擴充。在《核心Java API》中有大量這個技術的例子,諸如java.lang.String。將String類標記為final阻止了開發人員建立他們自己實現的長度方法。
更深入點說,如果類是final的,所有類的方法也是final的。Java編譯器可能會內聯所有的方法(這依賴於編譯器的實現)。在我的測試裡,我已經看到效能平均增加了50%。
4. 在任何可能的地方使用局部變數
屬於方法調用部分的自變數和聲明為此調用一部分的臨時變數儲存在棧中,這比較快。諸如static,執行個體(instance)變數和新的對象建立在堆中,這比較慢。局部變數的更深入最佳化依賴於你正在使用的編譯器或虛擬機器。
5. 停止小聰明
很多開發人員在腦子中編寫可複用和靈活的代碼,而有時候在他們的程式中就產生額外的開銷。曾經或者另外的時候他們編寫了類似這樣的代碼:
public void doSomething(File file) {
FileInputStream fileIn = new FileInputStream(file);
// do something
他夠靈活,但是同時他們也產生了更多的開銷。這個主意背後做的事情是操縱一個InputStream,而不是一個檔案,因此它應該重寫如下:
public void doSomething(InputStream inputStream){
// do something
6. 乘法和除法
我有太多的東東適用於摩爾法則——它聲明CPU功率每年成倍增長。“摩爾法則”表明每年由開發人員所寫的差勁的代碼數量三倍增加,划去了摩爾法則的任何好處。
考慮下面的代碼:
for (val = 0; val < 100000; val +=5) { shiftX = val 8; myRaise = val 2; }
如果我們狡猾的利用位移(bit),效能將會六倍增加。這是重寫的代碼:
for (val = 0; val < 100000; val += 5) { shiftX = val << 3; myRaise = val << 1; }
代替了乘以8,我們使用同等效果的左移3位。每一個移動相當於乘以2,變數myRaise對此做了證明。同樣向右移位相當於除以2,當然這會使執行速度加快,但可能會使你的東東以後難於理解;所以這隻是個建議
7. 用代碼有效處理記憶體溢出
OutOfMemoryError是由於記憶體不夠後普遍會遇到的問題,下面一段代碼能有效判斷記憶體溢出錯誤,並在記憶體溢出發生時有效回收記憶體
通過該方法可以聯想到有效管理串連池溢出,道理等同。
import java.util.*;
public class DataServer
{
private Hashtable data = new Hashtable();
public Object get (String key)
{
Object obj = data.get (key);
if (obj == null)
{
System.out.print (key + “ ”);
try
{
// simulate getting lots of data
obj = new Double[1000000];
data.put (key, obj);
}
catch (OutOfMemoryError e)
{
System.out.print (“/No Memory! ”);
flushCache();
obj = get (key);// try again
}
}
return (obj);
}
public void flushCache()
{
System.out.println (“Clearing cache”);
data.clear();
}
public static void main (String[] args)
{
DataServer ds = new DataServer();
int count = 0;
while (true) // infinite loop for test
ds.get (“” count+);
}
}
8. Lazy Loading (Lazy evaluation)在需要裝入的時候才裝入
static public long
factorial( int n ) throws IllegalArgumentException
{
IllegalArgumentException illegalArgumentException =
new IllegalArgumentException( “must be >= 0” );
if( n < 0 ) {
throw illegalArgumentException ;
} else if( ( n 0 ) || ( n 1 ) ) {
return( 1 );
} else (
return( n * factorial( n – 1 ) ) ;
}
最佳化後代碼
static public long
factorial( int n ) throws IllegalArgumentException
{
if( n < 0 ) {
throw new IllegalArgumentException( “must be >= 0” );
} else if( ( n 0 ) || ( n 1 ) ) {
return( 1 );
} else (
return( n * factorial( n – 1 ) ) ;
}
9. 異常在需要拋出的地方拋出,try catch能整合就整合
try {
some.method1(); // Difficult for javac
} catch( method1Exception e ) { // and the JVM runtime
// Handle exception 1 // to optimize this
} // code
try {
some.method2();
} catch( method2Exception e ) {
// Handle exception 2
}
try {
some.method3();
} catch( method3Exception e ) {
// Handle exception 3
}
已下代碼 更容易被編譯器最佳化
try {
some.method1(); // Easier to optimize
some.method2();
some.method3();
} catch( method1Exception e ) {
// Handle exception 1
} catch( method2Exception e ) {
// Handle exception 2
} catch( method3Exception e ) {
// Handle exception 3
}
10. For迴圈的最佳化
Replace…
for( int i = 0; i < collection.size(); i++ ) {
...
}
with…
for( int i = 0, n = collection.size(); i < n; i++ ) {
...
}
11. 字串操作最佳化
在對字串實行+操作時,最好用一條語句
// Your source code looks like…
String str = “profit = revenue( ” revenue
“ – cost( ” cost ““;
// 編譯方法
String str = new StringBuffer( ).append( “profit = revenue( “ ).
append( revenue ).append( “ – cost( “ ).
append( cost ).append( ““ ).toString( );
在迴圈中對字串操作時改用StringBuffer.append()方法
String sentence = “”;
for( int i = 0; i < wordArray.length; i++ ) {
sentence += wordArray[ i ];
}
最佳化為
StringBuffer buffer = new StringBuffer( 500 );
for( int i = 0; i < wordArray.length; i++ ) {
buffer.append( wordArray[ i ] );
}
String sentence = buffer.toString( );
12. 對象重用(特別對於大對象來說)
public
class Point
{
public int x;
public int y;
public Point( )
{
this( 0, 0 );
}
}
最佳化為:
public class Component
{
private int x;
private int y;
public Point getPosition( )
{
Point rv = new Point( ); // Create a new Point
rv.x = x; // Update its state
rv.y = y;
return rv;
}
}
// Process an array of Component positions…
for( int i = 0; i < componentArray.length; i++ ) {
Point position = componentArray[i].getPosition( );
// Process position value…
// Note: A Point object is created for each iteration
// of the loop…
}
可再次最佳化,僅使用一個類對象:)
public
class Component
{
private int x;
private int y;
public Point getPosition( Point rv )
{
if( rv == null) rv = new Point( );
rv.x = x; // Update its state
rv.y = y;
return rv;
}
// Create a single point object and reuse it…
Point p = new Point( );
for( int i = 0; i < componentArray.length; i++ ) {
Point position = componentArray[i].getPosition( p );
// Process position value…
// Note: Only one Point object is ever created.
}
13. j2ee相關
a) 盡量不要將大對象放到HttpSession或其他須序列化的對象中,並注意及時清空Session
b) 使用先行編譯語句prepareStatement代替createStatement
c) 儘可能使用串連池
d) 能使用Cache就使用Cache,具體實現可參考jive(Cache/Cacheable/CacheObject/CacheSizes/DefaultCache/LinkdList/LinkdListNode)或ofbiz(org.ofbiz.core.util. UtilCache.java)