???????2007年以來,從Geotools、PostGIS到JTS Topology Suite再到java優(yōu)化編程感受,看似過程相當(dāng)?shù)膹?fù)雜,而且相當(dāng)?shù)牧鑱y。呵呵,都是Geotools惹的禍呀,沒有辦法,為了能深度使用Geotools我只能研究JTS Topology Suite,在學(xué)習(xí)JTS過程中又有一些感想,呵呵,其實(shí)從目的而言跨度沒有那么大,只是一個磨刀不誤砍柴功的過程。?????
?????? 讀了一些JTS的源碼,呵呵,不對不是一些,而只是com.vividsolutions.jts.geom中的一部分那,還不到整個JTS Topology Suite源碼的1/10的代碼,對其實(shí)現(xiàn)思路可能還不是很清晰,但是看著大拿們寫的代碼還是讓我覺得很有收獲。這些收獲有我以前沒有感受的,也有以前知道原理但是就是不去重視的內(nèi)容。寫在這些文字當(dāng)作是對自己編程風(fēng)格的一篇械文。也希望在學(xué)習(xí)JTS的過程中能從大拿們的代碼中學(xué)習(xí)到更多的內(nèi)容,最終使得自己不僅僅學(xué)習(xí)這些編程大拿成果的使用,更使自己能提高自己的代碼質(zhì)量。
????? 作為自己對Java優(yōu)化編程感受的開篇,還是從最基礎(chǔ)的GC開始吧。呵呵,畢竟GC的存在是Java的特色之一,但是如果完全依托于JVM中的GC就想獲取良好的程序效率太困難了,這一點(diǎn)相信很多人都有一個清楚的認(rèn)識。如何合理的使用Java回收機(jī)制,如何讓GC更快的實(shí)施回收,我想也許很多程序員和我一樣都不是很重視,所以先來拋個磚,讓朋友們一起來批判一下。
???? (一下文字中有一些引用了曾經(jīng)看過的《JVM初探》、《Java的GC機(jī)制》等牛人文章,由于時(shí)間太長,很多定義我無法記住來源,所以就不一一標(biāo)明了。望原作者諒解!ps:標(biāo)題貌似是這個)
???? Java程序中的內(nèi)存管理機(jī)制是通過GC完成的,“一個對象創(chuàng)建后被放置在JVM的堆內(nèi)存中,當(dāng)永遠(yuǎn)不在應(yīng)用這個對象的時(shí)候?qū)?strong>JVM在堆內(nèi)存中回收。被創(chuàng)建的對象不能再生,同時(shí)也沒有辦法通過程序語句釋放”(這個是《Java的GC機(jī)制》中提到的定義,呵呵,還依稀記得)這就是GC對垃圾對象的定義。個人感覺這么解釋或許會比較快理解:在運(yùn)行環(huán)境中JVM會對兩種內(nèi)存進(jìn)行管理,一種是堆內(nèi)存(對象實(shí)例或者變量),一種是棧內(nèi)存(靜態(tài)或非靜態(tài)方法),而JVM所管理的內(nèi)存區(qū)域?qū)嶋H上就是堆內(nèi)存+棧內(nèi)存(MS:對象實(shí)例+實(shí)例化變量+靜態(tài)方法+非靜態(tài)方法),當(dāng)JVM在其所管理的內(nèi)存區(qū)域的中無法通過根集合到達(dá)對象的時(shí)候就會將此對象作為垃圾對象實(shí)施回收。
????? Java所有的對象都有一個生命周期:創(chuàng)建、使用、不可視、不可到達(dá)、回收釋放。先就從這個過程中結(jié)合昨天(2月9日)10點(diǎn)到今天(2月10日)凌晨2點(diǎn)看JTS代碼的一些感受,對自己來個自我批評吧,同時(shí)也對自己的一些經(jīng)驗(yàn)做一次總結(jié)(呵呵,在老爸家,無法上網(wǎng),都不知道什么時(shí)候可以帖到Blog去)
??????????先來看看我從JTS代碼中感受到我需要立刻改正的錯誤,雖說理論上我明白道理,但是我就是沒有去做過,也許這就是大師和大師兄的區(qū)別吧。。。汗自己一個。。。
????????? 我的代碼中曾經(jīng)出現(xiàn)過這樣的代碼:
????????? List alist=uSvr.getUserinfoList();
????????? for(int i=0;i<alist.size();i++){
???????????? //首先這里就有問題,這個是Crespo小兄弟給我提出的。最好使用for(int i=0 p=alist.size();i<p;i++),避免alist由于在循環(huán)體中發(fā)生變化時(shí)所帶來的問題,而且即便alist沒有發(fā)生變化,這么做也避免程序不斷去執(zhí)行size()方法所帶來的資源損耗。贊一個先,因?yàn)?a href="/crespo9907/" target="_blank">Crespo所提到的思路在JTS代碼中大師們都這么干,而且也絕對應(yīng)該這么干
????????????? Object obj=new Object();
????????????? //這里問題大了。創(chuàng)建對象第一忌:不要在循環(huán)體中創(chuàng)建對象。這種做法會在內(nèi)存中保存N份這個對象的引用//會浪費(fèi)大量的內(nèi)存空間(雖說內(nèi)存便宜,可以進(jìn)行硬件升級),同時(shí)JVM的GC機(jī)制會因?yàn)檫@些無謂的對象做大量//的回收工作,系統(tǒng)不慢都不行呀。。。好在這個問題很早以前就被我注意了,現(xiàn)在我的做法時(shí)在循環(huán)體外首先聲明一個空對象,然后在循環(huán)體內(nèi)new一個出來。
????????????? 。。。 。。。?
???????? }
????????現(xiàn)在我寫的代碼中出現(xiàn)這種情況
??????? public class Test{
?????????? Object obj=new Object();
?????????? public Test(){
????????????? obj=new Object();
???????????? //我的代碼將Object對象初始化了兩次。這個給內(nèi)存帶去的消耗絕對不比在循環(huán)體中創(chuàng)建對象來得小。創(chuàng)建對象第二忌:盡可能不要多次初始化對象。我這個問題也是恰好這兩天在給一個公司做一個SP數(shù)據(jù)轉(zhuǎn)發(fā)的開發(fā)中寫過的??碕TS的大師們的代碼在想流程的時(shí)候,偶然發(fā)現(xiàn)這個問題,但是那個汗呀。。真的。。汗流成河。。。
????????? }
?????? }
?????? 除了上面兩大忌諱外,對象在創(chuàng)建過程中還需要注意到以下問題。不采用過深的集成關(guān)系;訪問本地變量由于訪問類變量。這兩點(diǎn)很多書都有提到。
?????? 其實(shí)對象在使用階段的優(yōu)化,JTS的源碼給我了一種提示,那就是java.lang.ref的有效利用。這個包實(shí)際上我今天是第一次看,從來都沒有注意過這個包的使用問題,雖說它屬于java.lang核心開發(fā)API中的范疇。知道看到JTS源碼中出現(xiàn)這個代碼我才“帶著這玩意干什么的?”的疑問翻了一下API。
?????? 結(jié)合JTS源碼中的寫法以及所查閱的API的內(nèi)容,說說我的感受吧。首先在java.lang.ref包中最值得我們多關(guān)注的是如何合理的使用SoftReference以及WeakReference。也就是我么那如何將我們的對象置為軟引用和弱引用。在API上有軟引用以及弱引用的詳細(xì)解析,俺就不JY了,哪些人更不得了,都是火星派來介紹地球什么叫Java的外星人。很是先看看JTS代碼中大師們的程序風(fēng)格吧:
?
????Object?obj
=
new
?Object();

?????使用obj對象過程

????SoftReference?softReference
=
new
?SoftReference(obj);
//
將obj設(shè)置為軟引用類型
????obj
=
null
;
//
強(qiáng)制釋放引用

????
//
obj對象再次使用時(shí)應(yīng)該做的處理
????
if
(softReference
!=
null
)
{

????????obj
=
softReference.get();


?????}
else
{

???????obj
=
new
?Object();

????}
?
??????? 老實(shí)話,第一眼看到這樣的代碼我很驚訝,這tmd不是無聊嗎?用了obj對象就直接仍到SoftReference(哪時(shí)候還以為是JTS自己的一種緩存機(jī)制)不說還要強(qiáng)制釋放,第二次引用要么用get方法取,要么重新初始化,這不是沒事找事情嗎?唉,真是差距呀。。。OutOfMemory錯誤我一直都不在意,認(rèn)為那是硬件問題,我不管??墒强纯创髱焸?,為了更加有效的節(jié)約資源所作的工作吧,這些東西增加了少量的程序工作量,可是對于系統(tǒng)資源使用效率以及程序執(zhí)行效率的提升確實(shí)是很大的首先將對象設(shè)置為軟引用,然后強(qiáng)制釋放,將資源流出來給其他對象,當(dāng)程序需要在此使用這對象得時(shí)候要么從softReference(其實(shí)也可以看作是簡單緩存)中將原有對象還原,要么由于被GC回收了軟引用,再重新初始化一次。。。。(所以說研究出Java的人都是火星上來的,居然考慮得如此周密)
???? 弱引用方法河軟引用方法一樣。他們之間得區(qū)別在于弱應(yīng)用能更快得被GC給回收,畢竟軟應(yīng)用只是再內(nèi)存使用到達(dá)警戒水平的時(shí)候才會進(jìn)行。結(jié)合JTS目標(biāo)是處理空間數(shù)據(jù)以及空間拓?fù)?,它處理的對象很多都是一種Map結(jié)構(gòu)的對象,這種對象會占用大量的內(nèi)存空間,所以我們很好理解JTS為什么做了這么多我們平時(shí)再代碼中從來不注意的事情。但是這不代表因?yàn)槲覀兊南到y(tǒng)目標(biāo)和JTS不一樣,所以我們就可以不去做這些工作。
????????? 什么樣的對象可以將其認(rèn)定為不可視階段呢?舉個例子吧,在try{...}catch(Exception){...}代碼中,如果在try的代碼塊中聲明了一個obj,那么當(dāng)整個?try{...}catch(Exception){...}代碼段執(zhí)行完畢以后這個obj實(shí)際上就已經(jīng)屬于不可視階段了。在JTS源碼中我看到很多這樣的例子,大師們會在try代碼塊的最后多一句話:obj=null;實(shí)際上這種方式我也使用過,但是99%的時(shí)候都用所謂的項(xiàng)目時(shí)間緊張為借口忽略了。其實(shí)多這么一句話將obj對象置為空值可以快速的幫助JVM發(fā)現(xiàn)這個對象,并進(jìn)行回收以釋放資源。這里也有一部分代碼是使用的SoftReference來處理,呵呵,大師的風(fēng)格也不盡相同呀。
???????? 當(dāng)然,即便沒有這句話最終這個對象會被GC給回收,但是快速的釋放資源就可以有效的提高現(xiàn)有資源的利用效率,這個難道真的不應(yīng)該被我們這些使用高級編程語言進(jìn)行開發(fā)的程序員所提倡嗎?資源永遠(yuǎn)都是有限的,并非只是匯編或是做嵌入式開發(fā)的程序員才應(yīng)該關(guān)注程序使用資源的問題的!改編周星星名言-“程序不是這樣寫的!資源不是這么用的!”
??????????一個對象混到這份上就也該知足了,也該自覺一點(diǎn)輕輕走了算了,就算不走GC作為警察(戶籍警察)也會對此對象實(shí)施強(qiáng)制消戶了。。。一般情況下我還是不相信這個世界上有鬼存在的,那些怎么打也打不死的青銅圣斗士除外。。。。?
?
????? 其他方面據(jù)不完全統(tǒng)計(jì)的個人經(jīng)驗(yàn)而言,在開發(fā)過程中做到1、不提前創(chuàng)建對象,什么時(shí)候用什么時(shí)候創(chuàng)建;2、數(shù)組創(chuàng)建時(shí)盡可能避免顯示申請數(shù)據(jù)的內(nèi)存空間;3、當(dāng)對象占用資源大&對象數(shù)據(jù)穩(wěn)定&對象生命周期長可能的使用靜態(tài)變量;4、使用IDE編譯代碼的時(shí)候在程序的發(fā)布版本編譯時(shí)去掉IDE默認(rèn)的debug編譯模式(Eclipse:窗口-首選項(xiàng)-java-編譯器)、在需要同類大量對象的時(shí)候,使用對象池,數(shù)據(jù)庫連接池即在次范圍。。。 。。。同時(shí)沒有事情的時(shí)候多多學(xué)習(xí)大師的代碼,java開源社區(qū)有很多值得學(xué)習(xí)代碼風(fēng)格的源代碼等待著我們呢:)
?
附上代碼來解釋一下堆內(nèi)存和棧內(nèi)存的區(qū)別
(閱讀許可證:本實(shí)例代碼未滿18歲或家有女王者勿入,由本代碼引起的一切糾紛、人身傷害以及法律問題需閱讀者自行解決,原創(chuàng)作者
sinoly
不承擔(dān)任何責(zé)任。一旦閱讀本代碼,就表示你已接受本許可所提出的所有觀點(diǎn))
?
public
?
class
?我的老婆們
{

static
?Vector?老婆s
=
new
?Vector();
//
創(chuàng)建一個老婆的Vector序列
static
?
void
?娶個老婆()
{??????

??????PLMM?plmm?
=
?
new
?PLMM();
//
請首先new一個PLMM對象
??????老婆?wife
=
(老婆)plmm?;
//
不作此做出方法解釋,涉及到太多問題
??????老婆s.AddElement(老婆);
//
本方法多次運(yùn)行可實(shí)現(xiàn)了一夫多妻的宏偉目標(biāo),也是本人長期努力的方向
}
public
?
static
?
void
?main(String[]?args)
{

??????娶個老婆();
//
執(zhí)行此方法可能會帶來內(nèi)存溢出,請慎重!
}
}
?
棧內(nèi)存中存放的內(nèi)容:main,娶個老婆()
堆內(nèi)存中存放的內(nèi)容:我的老婆們,我的老婆們.老婆s,Vector,plmm,老婆
NOTE:堆內(nèi)存是在JVM啟動的時(shí)候創(chuàng)建,堆內(nèi)存分為新對象與老對象。對于新對象好像會分三個區(qū)域。當(dāng)優(yōu)先級最高的區(qū)域的堆棧滿了以后JVM將會進(jìn)行測試,測試內(nèi)容是那些對象不可到達(dá),不可到達(dá)的對象將會放入到老對象區(qū)域。同時(shí)JVM會將所有對象拷貝到另外兩個區(qū)域中,然后經(jīng)過一段時(shí)間依然沒有引用的對象會進(jìn)入老對象區(qū)域。對于老對象區(qū)域而言基本上就是等待被GC回收的對象了。(這些還是在大學(xué)時(shí)學(xué).net時(shí)候的知識,直接換成java定義我想其中概念應(yīng)該一樣。唉,大學(xué)畢業(yè)到現(xiàn)在快5年了--真的老了,唉!看著周圍83年甚至85年的同事,真的很傷自尊呀?。?/font>
ps:拜托各位看官千萬不要把前一段代碼和這個NOTE結(jié)合起來聯(lián)想,會死人的!