看JTS源碼,感受Java優化編程

原文地址:http://www.2cto.com/kf/201107/96528.html

        2007年以來,從GeotoolsPostGISJTS Topology Suite再到java優化編程感受,看似過程相當的複雜,而且相當的凌亂。呵呵,都是Geotools惹的禍呀,沒有辦法,爲了能深度使用Geotools我只能研究JTS Topology Suite,在學習JTS過程中又有一些感想,呵呵,其實從目的而言跨度沒有那麼大,只是一個磨刀不誤砍柴功的過程。    

       讀了一些JTS源碼,呵呵,不對,不是一些,而只是com.vividsolutions.jts.geom中的一部分,還不到整個JTS Topology Suite源碼的1/10的代碼,對其實現思路可能還不是很清晰,但是看着大拿們寫的代碼還是讓我覺得很有收穫。這些收穫有我以前沒有感受的,也有以前知道原理但是就是不去重視的內容。寫在這裏,這些文字當作是對自己編程風格的一篇械文。也希望在學習JTS的過程中能從大拿們的代碼中學習到更多的內容,最終使得自己不僅僅學習這些編程大拿成果的使用,更使自己能提高自己的代碼質量。

      作爲自己對Java優化編程感受的開篇,還是從最基礎的GC開始吧。呵呵,畢竟GC的存在是Java的特色之一,但是如果完全依託於JVM中的GC就想獲取良好的程序效率太困難了,這一點相信很多人都有一個清楚的認識。如何合理的使用Java回收機制,如何讓GC更快的實施回收,我想也許很多程序員和我一樣都不是很重視,所以先來拋個磚,讓朋友們一起來批判一下。

(以下文字中有一些引用了曾經看過的《JVM初探》、《Java的GC機制》等牛人文章,由於時間太長,很多定義我無法記住來源,所以就不一一標明瞭。望原作者諒解!ps:標題貌似是這個)

     Java程序中的內存管理機制是通過GC完成的,“一個對象創建後被放置在JVM的堆內存中,當永遠不在應用這個對象的時候將會被JVM在堆內存中回收。被創建的對象不能再生,同時也沒有辦法通過程序語句釋放”(這個是《Java的GC機制》中提到的定義,呵呵,還依稀記得)這就是GC對垃圾對象的定義。個人感覺這麼解釋或許會比較快理解:在運行環境中JVM會對兩種內存進行管理,一種是堆內存(對象實例或者變量),一種是棧內存(靜態或非靜態方法),而JVM所管理的內存區域實際上就是堆內存+棧內存(MS:對象實例+實例化變量+靜態方法+非靜態方法),當JVM在其所管理的內存區域的中無法通過根集合到達對象的時候就會將此對象作爲垃圾對象實施回收。

      Java所有的對象都有一個生命週期:創建、使用、不可視、不可到達、回收釋放。先就從這個過程中結合昨天(2月9日)10點到今天(2月10日)凌晨2點看JTS代碼的一些感受,對自己來個自我批評吧,同時也對自己的一些經驗做一次總結(呵呵,在老爸家,無法上網,都不知道什麼時候可以帖到Blog去)

  • 創建階段

          先來看看我從JTS代碼中感受到我需要立刻改正的錯誤,雖說理論上我明白道理,但是我就是沒有去做過,也許這就是大師和大師兄的區別吧。。。汗自己一個。。。

          我的代碼中曾經出現過這樣的代碼:

          List alist=uSvr.getUserinfoList();

          for(int i=0;i<alist.size();i++){

             //首先這裏就有問題,這個是Crespo小兄弟給我提出的。最好使用for(int i=0 p=alist.size();i<p;i++),避免alist由於在循環體中發生變化時所帶來的問題,而且即便alist沒有發生變化,這麼做也避免程序不斷去執行size()方法所帶來的資源損耗。贊一個先,因爲Crespo所提到的思路在JTS代碼中大師們都這麼幹,而且也絕對應該這麼幹

              Object obj=new Object();

              //這裏問題大了。創建對象第一忌:不要在循環體中創建對象。這種做法會在內存中保存N份這個對象的引用//會浪費大量的內存空間(雖說內存便宜,可以進行硬件升級),同時JVM的GC機制會因爲這些無謂的對象做大量//的回收工作,系統不慢都不行呀。。。好在這個問題很早以前就被我注意了,現在我的做法時在循環體外首先聲明一個空對象,然後在循環體內new一個出來。

            現在我寫的代碼中出現這種情況

        public class Test{

               Object obj=new Object();

               public Test(){

                   obj=new Object();

             //我的代碼將Object對象初始化了兩次。這個給內存帶去的消耗絕對不比在循環體中創建對象來得小。創建對象第二忌:儘可能不要多次初始化對象。我這個問題也是恰好這兩天在給一個公司做一個SP數據轉發的開發中寫過的。看JTS的大師們的代碼在想流程的時候,偶然發現這個問題,但是那個汗呀。。真的。。汗流成河。。。

          }

       }

       除了上面兩大忌諱外,對象在創建過程中還需要注意到以下問題。不採用過深的集成關係;訪問本地變量優於訪問類變量。這兩點很多書都有提到。

  • 使用階段

       其實對象在使用階段的優化,JTS的源碼給我了一種提示,那就是java.lang.ref的有效利用。這個包實際上我今天是第一次看,從來都沒有注意過這個包的使用問題,雖說它屬於java.lang核心開發API中的範疇。知道看到JTS源碼中出現這個代碼我才“帶着這玩意幹什麼的?”的疑問翻了一下API

       結合JTS源碼中的寫法以及所查閱的API的內容,說說我的感受吧。首先在java.lang.ref包中最值得我們多關注的是如何合理的使用SoftReference以及WeakReference。也就是我麼那如何將我們的對象置爲軟引用和弱引用。在API上有軟引用以及弱引用的詳細解析,俺就不JY了,哪些人更不得了,都是火星派來介紹地球什麼叫Java的外星人。很是先看看JTS代碼中大師們的程序風格吧:

     Object obj = new  Object();

     使用obj對象過程

    SoftReference softReference = new  SoftReference(obj); // 將obj設置爲軟引用類型

obj = null ; // 強制釋放引用

     // obj對象再次使用時應該做的處理

      if (softReference != null ) {

        obj = softReference.get();

     } else {

       obj = new  Object();

    }

        老實話,第一眼看到這樣的代碼我很驚訝,這tmd不是無聊嗎?用了obj對象就直接仍到SoftReference(哪時候還以爲是JTS自己的一種緩存機制)不說還要強制釋放,第二次引用要麼用get方法取,要麼重新初始化,這不是沒事找事情嗎?唉,真是差距呀。。。OutOfMemory錯誤我一直都不在意,認爲那是硬件問題,我不管。可是看看大師們,爲了更加有效的節約資源所作的工作吧,這些東西增加了少量的程序工作量,可是對於系統資源使用效率以及程序執行效率的提升確實是很大的首先將對象設置爲軟引用,然後強制釋放,將資源流出來給其他對象,當程序需要在此使用這對象得時候要麼從softReference(其實也可以看作是簡單緩存)中將原有對象還原,要麼由於被GC回收了軟引用,再重新初始化一次。。。。(所以說研究出Java的人都是火星上來的,居然考慮得如此周密)

     弱引用方法和軟引用方法一樣。他們之間得區別在於弱應用能更快得被GC給回收,畢竟軟引用只是再內存使用到達警戒水平的時候纔會進行。結合JTS目標是處理空間數據以及空間拓撲,它處理的對象很多都是一種Map結構的對象,這種對象會佔用大量的內存空間,所以我們很好理解JTS爲什麼做了這麼多我們平時再代碼中從來不注意的事情。但是這不代表因爲我們的系統目標和JTS不一樣,所以我們就可以不去做這些工作。

  • 不可視階段

          什麼樣的對象可以將其認定爲不可視階段呢?舉個例子吧,在try{...}catch(Exception){...}代碼中,如果在try的代碼塊中聲明瞭一個obj,那麼當整個 try{...}catch(Exception){...}代碼段執行完畢以後這個obj實際上就已經屬於不可視階段了。在JTS源碼中我看到很多這樣的例子,大師們會在try代碼塊的最後多一句話:obj=null;實際上這種方式我也使用過,但是99%的時候都用所謂的項目時間緊張爲藉口忽略了。其實多這麼一句話將obj對象置爲空值可以快速的幫助JVM發現這個對象,並進行回收以釋放資源。這裏也有一部分代碼是使用的SoftReference來處理,呵呵,大師的風格也不盡相同呀。

         當然,即便沒有這句話最終這個對象會被GC給回收,但是快速的釋放資源就可以有效的提高現有資源的利用效率,這個難道真的不應該被我們這些使用高級編程語言進行開發的程序員所提倡嗎?資源永遠都是有限的,並非只是彙編或是做嵌入式開發的程序員才應該關注程序使用資源的問題的!改編周星星名言-“程序不是這樣寫的!資源不是這麼用的!”

  • 不可到達/回收釋放階段

          一個對象混到這份上就也該知足了,也該自覺一點輕輕走了算了,就算不走GC作爲警察(戶籍警察)也會對此對象實施強制消戶了。。。一般情況下我還是不相信這個世界上有鬼存在的,那些怎麼打也打不死的青銅聖鬥士除外。。。。

      其他方面據不完全統計的個人經驗而言,在開發過程中做到以下幾個方面:

     1、不提前創建對象,什麼時候用什麼時候創建;

     2、數組創建時儘可能避免顯示申請數據的內存空間;

     3、當對象佔用資源大&對象數據穩定&對象生命週期長可能的使用靜態變量;

     4、使用IDE編譯代碼的時候在程序的發佈版本編譯時去掉IDE默認的debug編譯模式(Eclipse:窗口-首選項-java-編譯器)、在需要同類大量對象的時候,使用對象池,數據庫連接池即在次範圍。。。。。。同時沒有事情的時候多多學習大師的代碼,java開源社區有很多值得學習代碼風格的源代碼等待着我們呢:)

附上代碼來解釋一下堆內存和棧內存的區別

(閱讀許可證:本實例代碼未滿18歲或家有女王者勿入,由本代碼引起的一切糾紛、人身傷害以及法律問題需閱讀者自行解決,原創作者 sinoly 不承擔任何責任。一旦閱讀本代碼,就表示你已接受本許可所提出的所有觀點)

public   class  我的老婆們 {

static  Vector 老婆s = new  Vector(); // 創建一個老婆的Vector序列

static   void  娶個老婆() {     

      PLMM plmm  =   new  PLMM(); // 請首先new一個PLMM對象

      老婆 wife = (老婆)plmm ; // 不在此做出方法解釋,涉及到太多問題

      老婆s.AddElement(老婆); // 本方法多次運行可實現了一夫多妻的宏偉目標,也是本人長期努力的方向

}

public   static   void  main(String[] args) {

      娶個老婆(); // 執行此方法可能會帶來內存溢出,請慎重!

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章