一、引言
很多人可能在想這麼一個問題:Java有垃圾回收機制,那麼還存在內存洩露嗎?答案是肯定的,所謂的垃圾回收GC會自動管理內存的回收,而不需要程序員每次都手動釋放內存,但是如果存在大量的臨時對象在不需要使用時並沒有取消對它們的引用,就會吞噬掉大量的內存,很快就會造成內存溢出。
二、Java的垃圾回收機制
Java中的對象是在堆中分配,對象的創建有2中方式:new或者反射。對象的回收是通過垃圾收集器,JVM的垃圾收集器簡化了程序員的工作,但是卻加重了JVM的工作,這是Java程序運行稍慢的原因之一,因為GC為了能正確釋放對象,必須監控每一個對象的運行狀態,包括對象的申請、引用、被引用、賦值等,GC都要進行監控,監控對象的狀態是為了更加准確、及時地釋放對象,而釋放對象的根本原則就是該對象不再被引用。
三、Java中的內存洩露
內存洩露的對象有以下兩個特點:
① 這些對象是可達的,即在有向圖中存在通路可以與其相連。
② 這些對象是無用的,即程序以後都不會再使用這些對象。
public class Stack { private final static int MAX_ATTRIBUTE = 10; private Object[] arr; private int index = 0;; public void push(Object obj) { if (index > 9) throw new IllegalArgumentException(); arr[index] = obj; index++; } public Stack() { arr = new Object[MAX_ATTRIBUTE]; } public Object pop() { if (index < 0) throw new IllegalArgumentException(); return arr[--index]; } }
這個程序那裡發生了內存洩露呢?如果一個棧先增長然後收縮,那麼從棧中彈出來的對象將不會被當做垃圾回收,即使使用棧的程序不再引用這些對象,它們也不會被回收,因為棧內部維護這對這些對象的過期引用。
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; //Eliminate obsolete reference return result; }
查看本欄目
解決辦法:只要一個元素被彈出棧,那麼就將它的引用置為空,GC就會回收。
四、常見的內存洩露
① 像HashMap、Vector等靜態集合類的使用最容易引起內存洩露,因為這些靜態變量的生命周期與應用程序一致。
Vector<Object> vector = new Vector<Object>(10); for (int i = 1; i < 100; i++) { Object obj = new Object(); vector.add(obj); obj = null; }
把對象放入Vector中,如果僅僅釋放引用本身,但Vector仍然引用該對象,那麼這個對象對GC來說是不可回收的,如果要回收它,最簡單的辦事是將Vector對象設為null。
② 在Java程序中,我們經常要和監聽器打交道,通常調用諸如addXXXListener()等方法來增加監聽器,但往往忘記刪除這些監聽器,從而增加了內存洩漏的機會。
③ 使用連接池時,除了要顯式地關閉連接,還必須顯式地關閉Resultset和Statement對象。否則會造成大量的這些對象無法釋放,從而引起內存洩露。