Java的內存泄漏
查看代碼:
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2*size+1);
}
}
}
這段程序中并沒有很明顯的錯誤。但是這個程序中隱藏著一個問題。不嚴格的講,就是“內存泄漏”,隨著垃圾回收器活動的增加,或者由于內存占用的不斷增加,程序性能的降低會表現出來。在極端情況下,這種內存泄漏會導致磁盤交換,甚至導致OutOfMemoryError。
如果一個棧先增長,然后再收縮,那么,從棧中彈出的對象不會被當作垃圾回收,即使使用棧的程序不再引用這些對象,它們也不會被回收。這是因為,棧內部維護著對這些對象的過期引用。
過期引用:是指永遠不會被解除的引用
在上面的例子中,凡是在elements數組的活動部分之外的任何引用都是過期的。
在Java這樣的支持垃圾回收的語言中,內存泄漏是隱藏的。如果一個對象引用被無意識地保留起來了,那么垃圾回收機制不僅不會處理這個對象,而且也不會處理這個對象所引用的所有其他對象。
這里我們會對彈出的元素做一些簡單的處理:如下
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
只要類是自己管理內存,程序員就應該警惕內存泄漏問題。一旦元素被釋放掉,則該元素中包含的任何對象引用都應該被清空。
內存泄漏的另一個常見來源是緩存,一旦把對象引用放到緩存中,它就很容易被遺忘掉,從而使得它不再有用之后很長一段時間內仍然留在緩存內。
如果想要實現這樣的一個緩存:只要在緩存之外存在對某個項的健的引用,該項就有意義,那么就可以用WeakHashMap
代表緩存;當緩存中的項過期之后,它們就會被自動刪除。只有當所要的緩存項的生命周期是由該健的外部引用而不是由值決定時,WeakHashMap才有用處
內存泄漏的另一個常見來源是監聽器和其他回調,如果實現了一個API,客戶端在這個API中注冊回調,卻沒有顯式的取消注冊,那么除非采取某些動作,否則他們會聚集。確保回調立即被當做垃圾回收的最佳方法是只保存它們的弱引用。
內存泄漏往往是一個不明顯的Bug。BeCareful!!!