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