前一陣子跟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的成長趨勢逐步攀升那就代表程式中有空間忘了回收。在java中memory 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被呼叫a此arraylist會一直成長
}
}
see…概念應該不難吧。
所以請切記,程式的撰寫何時該回收,在garbage collection中,user反而常用VM會幫忙回收的藉口而淡忘自己該在記憶體使用上的注意力,這是一個優秀的工程師應該要小心的地方,另外善用調校工具也是一種選擇,但前提是:請瞭解發生memory leak的原理才是~~
留言列表