【摘要】隨著Java的廣泛應用,越來越多的關鍵企業系統也使用Java構建。作為Java核心運行環境的Java虛擬機JVM被廣泛地部署在各種系統平臺上。對Java應用的性能優化也越來越受到關注;談到Java應用的性能問題就不得不涉及到兩個方面:一是Java應用的構造是否是最優化的;二是對JVM的微調。本文將從對Java性能的優化做一些探討與研究。
【關鍵詞】性能優化JavaJVM
一談到性能優化,往往會被認為是應用開發和部署過程中或之后的事情,其實不然。如果想要構建一個最優化的系統,我們必須從該系統的需求分析和業務模型設計之初就要考慮到性能的最優化問題;當然對于一個已經構造好的系統來講,我們能做的只是在不改變系統代碼的前提下,盡量地在該系統的部署方案和運行環境上下功夫。由此,我們得出一個結論就是:所謂最優化是一個相對的概念,一個系統是否是最優化的,必須基于某個大前提來進行評判。因此,在進行優化分析之前一定要把握好前提條件是什么。以下我們將針對Java系統的性能優化,從代碼編寫和JVM兩個角度著手,總結一下常見的方法和思路。
1對象的生成和大小的調整
JAVA程序設計中一個普遍的問題就是沒有好好的利用JAVA語言本身提供的函數,從而常常會生成大量的對象(或實例)。由于系統不僅要花時間生成對象,以后可能還需花時間對這些對象進行垃圾回收和處理。因此,生成過多的對象將會給程序的性能帶來很大的影響。
1.關于String ,StringBuffer,和append應用實例
Java語言提供了對于String類型變量的操作。但如果使用不當,會給程序的性能帶來影響。如下面的語句:
String name=new String(“ZhangSan”);
System.out.println(name+“is my name”);
為了生成二進制的代碼,要進行如下的步驟和操作。生成新的字符串new String(string1);復制該字符串。加載字符串常量“ZhangSan”(string2);調用字符串的構架器(Constructor);保存該字符串到數組中(從位置0開始)從Java.io.PrintStream類中得到靜態的out變量,生成新的字符串緩沖變量new StringBuffer(STR_BUF_1);復制該字符串緩沖變量,調用字符串緩沖的構架器(Constructor);保存該字符串緩沖到數組中(從位置1開始),以string1為參數,調用字符串緩沖(StringBuffer)類中的append方法。加載字符串常量“is my name”(string3);以string3為參數,調用字符串緩沖(StringBuffer)類中的append方法。對于STR_BUF_1執行toString命令(string4)。調用out變量中的println方法,輸出結果。
由此可以看出,這兩行簡單的代碼,就生成了string1,string2,string3,string4和STR_BUF_1五個對象變量。這些生成的類的實例一般都存放在堆中。堆要對所有類的超類,類的實例進行初始化,同時還要調用類極其每個超類的構架器。而這些操作都是非常消耗系統資源的。因此,對對象的生成進行限制,是完全有必要的。經修改,上面的代碼可以用下面的代碼來替換。
StringBuffer name=new StringBuffer(“ZhangSan”);
System.out.println(name.append(“is my name”). toString());
修改后的語句系統將進行如下的操作:生成新的字符串緩沖變量new StringBuffer(STR_BUF_1);復制該字符串緩沖變量,加載字符串常量“ZhangSan”(string1);調用字符串緩沖的構架器(Constructor);保存該字符串緩沖到數組中(從位置1開始),從Java.io. PrintStream類中得到靜態的out變量,加載STR_BUF_1;加載字符串常量”is my name”(string2);以string2為參數,調用字符串緩沖(StringBuffer)實例中的append方法。對于STR_BUF_1執行toString命令。(string3),調用out變量中的println方法,輸出結果。由此可以看出,經過改進后的代碼只生成了四個對象變量:string1,string2,string3和STR_BUF_1.你可能覺得少生成一個對象不會對程序的性能有很大的提高。但下面的代碼段2的執行速度將是代碼段1的2倍。因為代碼段1生成了八個對象,而代碼段2只生成了四個對象。
代碼段1:String name= new StringBuffer(“HuangWeiFeng”);
name+=”is my”;name+=”name”;
代碼段2:StringBuffer name=new StringBuffer(“HuangWeiFeng”);name.append(“is my”);name.append(“name.”).toString();
2.盡可能的使用靜態變量
如果類中的變量不會隨他的實例而變化,就可以定義為靜態變量,從而使他所有的實例都共享這個變量。
3.不要對已生成的對象作過多的改變
對于一些類(如:String類)來講,寧愿在重新生成一個新的對象實例,而不應該修改已經生成的對象實例。
4.生成對象時,要分配給它合理的空間和大小
Java中的很多類都有它的默認的空間分配大小。對于StringBuffer類來講,默認的分配空間大小是16個字符。如果在程序中使用StringBuffer的空間大小不是16個字符,那么就必須進行正確的初始化。
5.避免生成不太使用或生命周期短的對象或變量。
對于這種情況,因該定義一個對象緩沖池。以為管理一個對象緩沖池的開銷要比頻繁的生成和回收對象的開銷小的多。
2編寫性能高效的Java代碼
1.同步
為了減少JVM和操作系統中的爭用,應該只在可行的情況下才使用同步方法。不要將同步方法放到循環結構中。
2.數據結構
作為一條通用規則,在更簡單的數據結構能滿足需要的地方,應該避免使用更復雜的數據結構。例如,在可以使用數組的地方不要使用向量。使用最有效的方法搜索元素,并將元素插入數據結構中,比如說,在向量的結尾處添加和刪除元素,以便獲得更好的性能。
3.盡可能使用堆棧變量
如果您頻繁存取變量,就需要考慮從何處存取這些變量。變量是static變量,還是堆棧變量,或者是類的實例變量?變量的存儲位置對存取它的代碼的性能有明顯的影響。JVM是一種基于堆棧的虛擬機,因此優化了對堆棧數據的存取和處理。所有局部變量都存儲在一個局部變量表中,在Java操作數堆棧中進行處理,并可被高效地存取。存取static變量和實例變量成本更高,因為JVM必須使用代價更高的操作碼,并從常數存儲池中存取它們。通常,在第一次從常數存儲池中訪問static變量或實例變量以后,JVM將動態更改字節碼以使用效率更高的操作碼。盡管有這種優化,堆棧變量的存取仍然更快。
4. finalize函數
finalize是位于Object類的一個方法,該方法的訪問修飾符為protected,由于所有類為Object的子類,因此用戶類很容易訪問到這個方法。由于,finalize函數沒有自動實現鏈式調用,我們必須手動的實現,因此finalize函數的最后一個語句通常是super.finalize()。通過這種方式,我們可以從下到上實現finalize的調用,即先釋放自己的資源,然后再釋放父類的資源。
通常,finalize用于一些不容易控制、并且非常重要的資源的釋放,例如一些I/O的操作,數據的連接。這些資源的釋放對整個應用程序是非常關鍵的。在這種情況下,程序員應該以通過程序本身管理(包括釋放)這些資源為主,以finalize函數釋放資源方式為輔,形成一種雙保險的管理機制,而不應該僅僅依靠finalize來釋放資源。