1. 程式人生 > >不得不知道的Java記憶體溢位之在經常呼叫的方法內不要new大物件

不得不知道的Java記憶體溢位之在經常呼叫的方法內不要new大物件

        相信只要你看過Java的書,你一定會看到類似這樣的話:千萬不要假想內在處理器已經幫你把記憶體處理好了去做事。

        看過,真的就只是看過。和C還有C++相比,Java在大部分時間都可以省去了對記憶體的手動釋放操作,不得不說,這個真的挺好使的,因為當初看C++的時候,用個數組,需要釋放內在,真心鬧不懂啊,這也是為什麼當初沒有真正走進程式設計世界的原因吧。

        但出來混,問題要還的。如果你程式碼寫的不好,或者演算法有問題,那麼記憶體溢位的陰影就無時無刻不籠罩你。前兩天,就因為自己懶而遇到了內在溢位的問題。

        上篇文章說到了遞迴呼叫,下面看下我遇到問題的核心演算法:


import(ListOfElement){
	for(iterator iter=ListOfElement.iterator();iter.hasNext();){
		Element ele=iter.next();
		Object o=Xml2Obj(ele);	//Xml2Obj是一個將Element轉成Obj的方法
		addObject(o);  //注意這行程式碼呼叫的方法
		if(ele.elements()!=null && ele.elements().size()>0){
			import(ele.elements());
		}
	}
}
addObject(object){
	List list=object.getSomething();
	do others
}

        網上說遞迴呼叫容易造成內在溢位,後來我改成了非遞迴,如下:
import(ListOfElement){
	Queue queue=new LinkedList();	//採用Java裡面的佇列(LILO)
	for(iterator iter=ListOfElement.iterator();iter.hasNext();){
		queue.add(iter.next());
		while(!queue.isEmpty()){
			Element ele=queue.poll();  //poll和remove的區別是如果為空,remove會拋異常,而poll返回空
			Object o=Xml2Obj(ele);
			addObject(o);  //注意這行程式碼呼叫的方法
			if(ele.elements()!=null && ele.elements().size()>0){				
				for(iterator iterChild=ele.elements();iterChild.hasNext){
					queue.add(iterChild.next());
				}
			}
		}
	}
}

        但是,受傷並沒有好一些,記憶體溢位依然存在,心都碎了。後來又在網上找記憶體溢位的原因,一個一個的進行了對比,發現有人說不要在經常呼叫的方法裡建立對你,尤其是迴圈裡。

這點說的不就是我麼?那個addObject經常被呼叫,而且new了一個比較大的物件(List),並且還是在迴圈裡,然後我就把這個List手動釋放了一下,注意,List的釋放需要先呼叫clear,然後再把其設定成null,如下:

addObject(object){
	List list=object.getSomething();
	do others
	list.clear();
	list=null;
}

        現在,再執行發現很快就執行完成,並且記憶體也沒有用多少。於是又實驗了一下:改回遞迴,而這個方法記憶體釋放,發現依然記憶體溢位。這也就說明了遞迴呼叫確實容易造成記憶體溢位。

        通過這個例子,想說明的是:當你發現記憶體溢位後,不要第一時間就是加記憶體,造成記憶體溢位最大的可能就是你的程式碼沒有優化,也通過這個問題讓我又重新溫習了一下那好久沒有翻過的資料結構與演算法。

        最後,向大家推薦一下讓我找到思路的部落格注意Java記憶體洩漏。同時也推薦大家沒事的時候,將看看資料結構與演算法這本書,真的很好~~~