1. 程式人生 > >Java記憶體溢位與記憶體洩漏

Java記憶體溢位與記憶體洩漏

記憶體溢位:記憶體溢位就是指在指定大小的記憶體空間,寫入了超出大小的資料(越界).或者沒有足夠的記憶體,供程式分配。


記憶體洩漏:程式在執行過程中動態申請的記憶體空間不再使用後沒有及時釋放,從而很可能導致應用程式記憶體無線增長。更廣義的記憶體洩露包括未對系統的資源的及時釋放,比如控制代碼等。


結果: 1.記憶體溢位:程式將停止
      2.記憶體洩漏:程式不會停止但是效率會降低,記憶體洩漏過多也可能造成記憶體溢位。


Java 記憶體洩漏:
發生情況及解決方法:
  1.靜態集合類,如HashMap 和Vector
            靜態集合類的生命週期和程式一致,一次集合物件不用時應及時將其設為Null。
  2.各種連線,如資料庫連線,網路連線,I/O連線。
            連線不用時應將其Close,否則GC不會回收相應的物件。
  3.監聽器。
            在釋放物件時,必須刪除相應的監聽器。
  4.變數的不合理作用於。若物件僅在區域性使用,卻被宣告在更大的作用範圍當中,就可能造成記憶體洩漏,或者沒有及時的將物件設為null,也會造成記憶體洩漏。
            物件宣告,應該和其使用範圍一致,並且使用結束後及時設為Null。


避免方法:
         1) 儘早釋放無用物件的引用。


          好的辦法是使用臨時變數的時候,讓引用變數在退出活動域後自動設定為null,暗示垃圾收集器來收集該物件,防止發生記憶體洩露。


          2) 程式進行字串處理時,儘量避免使用String,而應使用StringBuffer。


          因為每一個String物件都會獨立佔用記憶體一塊區域,如:


         3) 儘量少用靜態變數。


         因為靜態變數是全域性的,GC不會回收。


         4) 避免集中建立物件尤其是大物件,如果可以的話儘量使用流操作。


          JVM會突然需要大量記憶體,這時會觸發GC優化系統記憶體環境; 


         5) 儘量運用物件池技術以提高系統性能。


          生命週期長的物件擁有生命週期短的物件時容易引發記憶體洩漏,例如大集合物件擁有大資料量的業務物件的時候,可以考慮分塊進行處理,然後解決一塊釋放一塊的策略。


         6) 不要在經常呼叫的方法中建立物件,尤其是忌諱在迴圈中建立物件。
 
          可以適當的使用hashtable,vector 建立一組物件容器,然後從容器中去取那些物件,而不用每次new之後又丟棄。


         7) 優化配置




Java 記憶體溢位:
     
區域  1.堆記憶體溢位  java.lang.OutOfMemoryError:Java heap space
      2.棧溢位
      3.方法區(包括常量池)溢位  java.lang.OutOfMemoryError: PermGen space 載入了過多的類,jar等
      4.直接記憶體溢位
 
情況:1、在程式中存在死迴圈,或者迴圈過多,而產生了過多重複的物件的例項;
     2、存在物件的引用,使用完後沒有清除,導致JAVA虛擬機器不能回收;如集合的使用。
     3、一次操作時,在記憶體中載入了大量的資料;(演算法空間複雜度過高) 
     4.使用的第三方軟體中的BUG; 
     5.啟動引數記憶體值設定的過小;
原則上來說,在JAVA中,由於它的自動垃圾回收機制,出現記憶體溢位的可能性並不是很大。




排查:
    1.檢查程式碼中是否有死迴圈或遞迴呼叫。 
    2.檢查是否有大迴圈重複產生新物件實體。 
    3.檢查對資料庫查詢中,是否有一次獲得全部資料的查詢。一般來說,如果一次取十萬條記錄到記憶體,就可能引起記憶體溢位。這個問題比較隱蔽,在上線前,資料庫中資料較少,不容易出問題,上線後,資料庫中資料多了,一次查詢就有可能引起記憶體溢位。因此對於資料庫查詢儘量採用分頁的方式查詢。 
    4.檢查List、MAP等集合物件是否有使用完後,未清除的問題。List、MAP等集合物件會始終存有對物件的引用,使得這些物件不能被GC回收。 




解決:  1.從程式碼層面進行優化完善,儘量避免該情況發生;
          一:
            1.對那些靜態(static)的物件要特別留神,特別是型別為Map,List,Set的,靜態的變數會一直駐存在記憶體中,生命週期比較長,不會被垃圾器回收。
            2.對於程式碼,要審查是否生成了大量的冗餘的物件,還有一些邏輯業務處理的類,
          演算法是否過於複雜,調整演算法,對於程式碼認真審查,再仔細重構一遍程式碼,能提高程式碼質量,提高程式執行穩定性。
           
          3.Java中的記憶體溢位大都是因為棧中的變數太多了。其實記憶體有的是。建議不用的儘量設成null以便回收,多用區域性變數,少用成員變數。
          4.在我的程式中對靜態變數的優化後,使程式佔用記憶體量至少提升了5k-10k。所以也不容忽視。


          二:
            還有就是String類相關的東西:
          1.字串累加的時候一定要用StringBuffer的append方法,不要使用+操作符連線兩個字串。差別很大。而且在迴圈或某些重複執行的動作中不要去建立String物件,因為String物件是要用StringBuffer物件來處理的,一個String物件應該是產生了 3個物件(大概是這樣:))。
          2.字串length()方法來取得字串長度的時候不要把length放到迴圈中,可以在迴圈外面對其取值。(包括vector的size方法)。特別是迴圈次數多的時候,儘量把length放到迴圈外面。


             3 寫程式碼的時候處理記憶體溢位   
                   try{   
                      //do   sth    
                   }catch   (outofmemoryerror   e){//可以用一個共通函式來執行.   
                    system.out.print   (“no   memory!   ”);   
                    system.gc();   
                     //do   sth   again   
                  ....   
                   }          
             4.對於頻繁申請記憶體和釋放記憶體的操作,還是自己控制一下比較好,但是System.gc()的方法不一定適用,最好使用finallize強制執行或者寫自己的finallize方法。 Java 中並不保證每次呼叫該方法就一定能夠啟動垃圾收集,它只不過會向JVM發出這樣一個申請,到底是否真正執行垃圾收集,一切都是個未知數。


       2.調整優化JVM,伺服器配置(調整引數): 


         1) 設定-Xms、-Xmx相等;


         2) 設定NewSize、MaxNewSize相等;


         3) 設定Heap size, PermGen space(永久代空間)