前一陣子跟member在做系統效能調校,發現只要跑久一點就會出現heap out of memory,初步認定應該是有地放造成了memory leak的問題,所以開始去找了一個可能的方向,也還算順利後來問題解掉了,在此也提供一些心得,讓其他飽受memory leak這種問題的使用者參考~~

 

確認清楚系統的定位。我常說遇到問題先看想清楚想達到的目的之本質為何:我的系統是交作業,跑一次就結束,下次需要在重新開啟即可?還是系統是一種Server的服務,需長期運作不能重開機。這兩者有很大的差別,前者我稱為間隔性系統後者我稱為連續性系統。當這兩者遇到heap out of memory的時後,處理上有其差異性,若以複雜度來看連續性系統將較難排除,當然這也是本文章要說明的重點。

 

間隔性系統請直接調整JVM中的Xmx上限吧。由於間隔性程式是允許執行完此java process就停止,所以其實memory是否有leak影響不大,只要記憶體裝的下一直擴張的容量即可,反正執行完時跟此VM相關的所有記憶體將被回收。下次重新執行時,又是重零開始起算,故問題不大。除非,就算調整了Xmx此記憶體上限時系統依舊會發生heap out of memory很多人或許會問我,為何贊成這種治標不治本的方法,原因很簡單:若系統每次都可以重開,就算記憶體沒關好,但下次會歸零的話,以解決問題的成本效益,調整一個參數就可以解掉一個難纏的問題也是一件好事囉。畢竟徹底的改善是好事,但在實務上也請把調校時間的成本一併算進去思考一下,有時候沒有意義的完美解決方案,反而比偷吃步來的不切實際。

 

連續性系統請耐下性子,徹底發覺memory leak所在地吧。由於系統需長時間服務,又重啟系統可能造成線上使用者的不便,故此情況下不管成本多高也請耐下性子把問題找出來。要勘查memory leak的方式很簡單,當執行的區域不變(例如一直執行某個網頁),隨著執行時間越久若memory的成長趨勢逐步攀升那就代表程式中有空間忘了回收。javamemory leak的起因通常是因為存在static修飾詞且為collection類型之型別屬性,由於此屬性為static故為類別所擁有,而類別是從VM一起動就經由class loader將之載入,在執行過程中他會一直保存在記憶體中,當此static屬性在程式執行過程中有去定時或不定時的加入物件於其中,而且沒有自行固定將之回收的話,memory leak的問題就會開始產生,因為static屬性中的資料會一直存在,要嘛你自己把他砍了,不然就是等JVM處理完畢才一併清除。當然在連續性系統中是不能等JVM處理完畢的,所以請找出static屬性去檢視問題吧。以下例而言系統在跑一定時間之後就會產生heap out of memory

public class Test3 {

  public static ArrayList a = new ArrayList(); //a是原兇

 

  public static void main(String[] args) {

    int i=0;

    while(true) { //while true下去跑,一下子就heap out of memory

      i++;

      if(i % 1000 == 0) //懶得每次都印出i,所以跑1000次才印1

System.out.println(i); //沒調Xmx下跑了三百多萬次就掛了

      Test3.method1();

    }

  }

 

  public static void method1() {

    a.add(new Integer(10)); //method1被呼叫aarraylist會一直成長

  }

}

see…概念應該不難吧。

 

所以請切記,程式的撰寫何時該回收,在garbage collection中,user反而常用VM會幫忙回收的藉口而淡忘自己該在記憶體使用上的注意力,這是一個優秀的工程師應該要小心的地方,另外善用調校工具也是一種選擇,但前提是:請瞭解發生memory leak的原理才是~~

 

arrow
arrow
    全站熱搜

    劉逸 發表在 痞客邦 留言(1) 人氣()