- 相關(guān)推薦
J2EE系統優(yōu)化:對象與循環(huán)
優(yōu)化一般意義上說(shuō)是提高已有系統的性能,減少如內存、數據庫、網(wǎng)絡(luò )帶寬等資源的占用,是在系統開(kāi)發(fā)告一段落的前提下進(jìn)行。一般是通過(guò)壓力測試或具體使用發(fā)現性能方面的問(wèn)題,然后尋找性能瓶頸,并結合項目進(jìn)度、人員安排、技術(shù)儲備等因素,提出相應的優(yōu)化策略。
下面結合一些案例,進(jìn)行具體的討論,總結出兩個(gè)有代表性的條例:
條例一:盡量重用對象,避免創(chuàng )建過(guò)多短時(shí)對象
對象在面向對象編程中隨處可見(jiàn),甚至可以毫不夸張的說(shuō)是:“一切都是對象”。如何更好的創(chuàng )建和使用對象,是優(yōu)化中要考慮的一個(gè)重要方面。筆者將對象按使用分為兩大類(lèi):獨享對象和共享對象。獨享對象指由某個(gè)線(xiàn)程單獨擁有并維護其生命周期的對象,一般是通過(guò)new 創(chuàng )建的對象,線(xiàn)程結束且無(wú)其它對這個(gè)對象的引用,這個(gè)對象將由垃圾收集機制自動(dòng)GC。共享對象指由多個(gè)線(xiàn)程共享的對象,各線(xiàn)程保持多個(gè)指向同一個(gè)對象的引用,任何對這個(gè)對象的修改都會(huì )在其它引用上得到體現,共享對象一般通過(guò)Factory工廠(chǎng)的getInstace()方法創(chuàng )建,單例模式就是創(chuàng )建共享對象的標準實(shí)現。獨享對象由于無(wú)其它指向同一對象的引用,不用擔心其它引用對對象屬性的修改,在多線(xiàn)程環(huán)境里,也就不需要對其可能修改屬性的方法加以同步,減少了出錯的隱患和復雜性,但由于需要為每個(gè)線(xiàn)程都創(chuàng )建對象,增加了對內存的需求和JVM GC的負擔。共享對象則需要進(jìn)行適當的同步(避免較大的同步塊,同時(shí)防止死鎖)。
還有幾種特殊對象:不變對象和方法對象。不變對象指對象對外不含有修改對象屬性的方法(如set方法),外部要修改屬性只能通過(guò)new新的實(shí)例來(lái)實(shí)現。不變對象最大的好處就是無(wú)需擔心屬性被修改,避免了潛在的bug,并能無(wú)需任何額外工作(如同步)就很好的工作在多線(xiàn)程環(huán)境下。如jdk的String對象就是典型的不變對象。方法對象簡(jiǎn)單的說(shuō)就是僅包含方法,不含有屬性的對象。由于沒(méi)有對象屬性,方法中無(wú)需進(jìn)行修改屬性的操作,也就能采用static方法或單例模式,避免每次使用都要new對象,減少對象的使用。
那么該如何確定創(chuàng )建何種對象,這就要結合對象的使用方式和生命周期、對象大小、構建花銷(xiāo)等方面來(lái)綜合考慮。如果對象生命周期較長(cháng),會(huì )存在修改操作,不能容忍其它線(xiàn)程對其的修改,就應該采用獨享對象,如常見(jiàn)的Bean類(lèi)。而如果對象生命周期較長(cháng),且能為各個(gè)線(xiàn)程共享,就可以考慮共享對象。共享有2種常見(jiàn)情況,一種是系統全局對象,如配置屬性等,各個(gè)線(xiàn)程應該引用同一對象,任何對這個(gè)對象的修改都會(huì )影響其它線(xiàn)程;另一種是由于對象創(chuàng )建開(kāi)銷(xiāo)較大,各線(xiàn)程對此對象是瞬時(shí)訪(fǎng)問(wèn),且無(wú)需再次讀取其屬性,如常見(jiàn)的Date 對象,一般這種對象的使用是瞬時(shí)的,比如把它format成String,如果每次創(chuàng )建然后等待GC就會(huì )浪費大量?jì)却婧虲PU時(shí)間,較好做法就是做成共享對象,各個(gè)線(xiàn)程先set再使用,注意對進(jìn)行set并訪(fǎng)問(wèn)的方法要同步。不變對象一般使用在對象創(chuàng )建開(kāi)銷(xiāo)較小(屬性較少,類(lèi)層次較少),且需要能自由共享的情形。如一個(gè)對象里的常量對象,使用public static final AAA=new AAA(…) 創(chuàng )建。方法對象使用較廣,如Util類(lèi)、DAO類(lèi)等,這些對象提供操作其它對象(一般是bean對象)的接口,能對系統在層次和功能上進(jìn)行解耦合。
條例二:在循環(huán)處,多下功夫
循環(huán)作為程序編寫(xiě)的基本語(yǔ)法,可以說(shuō)是隨處可見(jiàn)。一些小的細節能帶來(lái)性能上的提升,而對循環(huán)體的一些改寫(xiě),能帶來(lái)性能的大幅提升。
比如最簡(jiǎn)單的List遍歷,會(huì )有這樣的寫(xiě)法:for(int i=0;i
同樣是對List的操作,如果要在遍歷同時(shí)進(jìn)行增加和刪除操作,代碼如下:for(int i=0,j=l.size();i=0;i--){l.remove(i);}。經(jīng)過(guò)測試,如果采用ArrayList,兩種寫(xiě)法在循環(huán)次數較少時(shí)沒(méi)有太大的區別,循環(huán)次數為1000,均為1ms以?xún),次數?0000,前一種為60ms左右,后一種為1ms以?xún),,而次數上?00000,前一種為6000ms左右,后一種為15ms,隨著(zhù)循環(huán)次數的增多,后一種較前一種的效率優(yōu)勢明顯提高。
這是由Collection庫ArrayList的實(shí)現決定的,以下是jdk1.3的ArrayList源碼:
從中我們可以看出,numMoved代表了需要進(jìn)行arraycopy操作的數量,它是由remove的位置決定的,如果index=0,也就是刪除第一個(gè)元素,則需要arraycopy后面的所有數據,而如果index=size-1,則只需將最后一個(gè)元素設為null即可。所以從后面向前循環(huán)remove是比較好的寫(xiě)法。
如果List中的確存在較多的add或remove操作,且容量較大(如存儲幾萬(wàn)個(gè)對象),則應該采用LinkedList作為實(shí)現。LinkedList內部采用雙向鏈表作為數據結構,比ArrayList占用較多內存空間,且隨機訪(fǎng)問(wèn)操作較慢(需要從頭或尾循環(huán)到相應位置),但插入刪除操作很快(僅需進(jìn)行鏈表操作,無(wú)須大量移動(dòng)或拷貝)。
對于List操作如果循環(huán)規模較小,其實(shí)對性能影響非常小(ms級),遠遠不是性能瓶頸所在。但心中有著(zhù)優(yōu)化的意識,并力求寫(xiě)出簡(jiǎn)潔高效的程序應該是我們每個(gè)程序員的追求。而且一旦在循環(huán)規模較大時(shí),如果有了這些意識,也就能有效的消除性能隱患。
再舉一個(gè)與優(yōu)化無(wú)關(guān)但確實(shí)可能成為性能殺手(可以說(shuō)是bug)的循環(huán)的例子。下面是源代碼:
這個(gè)代碼意圖很清楚,就是將一個(gè)InputStream流讀到一個(gè)byte數組中去。它使用read方法循環(huán)讀取InputStream,該方法返回讀取的字節數。正常情況下,該循環(huán)運行良好,當totalRead=m_totalBytes時(shí),結束循環(huán),byte數組被正常填充。但如果仔細看一下InputStream的read方法的說(shuō)明,了解一下其返回值就會(huì )發(fā)現,返回值可能為-1,即已讀到InputStream末尾再繼續讀時(shí)。如果發(fā)生讀取異常,可能出現這個(gè)問(wèn)題,而這個(gè)循環(huán)沒(méi)有檢查readBytes值是否為-1就往totalRead上加,這樣再次進(jìn)入循環(huán)體繼續讀取InputStream,又返回-1,繼續循環(huán)。如此循環(huán)直到int溢出才會(huì )跳出循環(huán)。而這個(gè)循環(huán)也就成了實(shí)實(shí)在在的CPU殺手,可以占去大量的CPU時(shí)間(取決于操作系統)。其實(shí)解決很簡(jiǎn)單,對readBytes進(jìn)行判斷,如果為-1則跳出循環(huán)。
這個(gè)例子告訴我們:對循環(huán)一定要搞清循環(huán)的循環(huán)規模、每次循環(huán)體執行時(shí)間、循環(huán)結束條件包括異常情況等,只有這樣才能寫(xiě)出高效且沒(méi)有隱患的代碼。
【J2EE系統優(yōu)化:對象與循環(huán)】相關(guān)文章:
電腦系統怎么優(yōu)化01-05
msconfig系統配置優(yōu)化10-26
XP系統優(yōu)化四大技巧09-04
臨床執業(yè)醫師考點(diǎn):循環(huán)系統疾病09-30
中醫助理醫師考點(diǎn):循環(huán)系統疾病08-08
讓W(xué)in10系統運行更流暢的優(yōu)化技巧08-10
系統架構設計師:優(yōu)化顯示速度01-11
什么是J2EE07-05