內存泄露是一個很不容易發現的問題,在不少狀況下,咱們都很容易忽略,Java的垃圾回收確實幫助咱們解決了很多內存管理的問題,可是,這並不意味着咱們就能夠徹底依賴Java的垃圾回收。咱們仍是在編寫程序的時候須要考慮內存管理的問題。下面咱們先來看一個例子,java
public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } /** * Ensure space for at least one more element, roughly doubling the capacity * each time the array needs to grow. */ private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }
可是這個程序中隱藏着一個問題。不嚴格地講,這段程序有一個"內存泄漏(Memory Leak)",隨着垃圾回收器活動的增長,或者因爲內存佔用的不斷增長,程序性能的下降會逐漸表現出來。若是一個棧先是增加,而後再收縮,那麼,從棧中彈出來的對象將不會被看成垃圾回收,即便使用棧的程序再也不引用這些對象,它們也不會被回收。這是由於,棧內部維護着對這些對象的過時引用(obsolete reference)。所謂的過時引用,是指永遠也不會再被解除的引用。程序員
這類問題的修復方法很簡單:一旦對象引用已通過期,只需清空這些引用便可。對於上述例子中的Stack類而言,只要一個單元被彈出棧,指向它的引用就過時了。pop方法的修訂版本以下所示:緩存
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; }
通常而言,只要類是本身管理內存,程序員就應該警戒內存泄漏問題。一旦元素被釋放掉,則該元素中包含的任何對象引用都應該被清空。性能
內內存泄露的另外一個常見來源是緩存,所以這時要用一個線程按期清緩存或在加入時清最少使用的緩存對象。在1.4發行版中,能夠用java.util.LinkedHashMap的revmoveEldestEntry方法來實現後一方案。
spa