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