java 雖然有垃圾回收機制,但是對于被引用的對象,就算我們已經不再使用,它的回收機制可能也不會進行回收,稱之為內存泄漏。
書中有這樣一個例子,先看代碼:
public class Stack { public Object[] elements;//原文是PRivate,便于測試改成public private int size = 0; private static final int DEFAULT_INITIAL_CAPCITY = 10; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPCITY]; } 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); }}代碼很簡單,入棧、出棧,程序并沒有明顯的錯誤,無論怎么測試,都能通過,但是這段程序有一個內存泄漏。 當入棧,然后出棧,從棧中彈出來的對象將不會被當做垃圾回收,即使使用棧的程序不再引用這些對象,他們也不會被回收。這是因為棧內部維護著對這些對象的過期引用,在這個例子中,凡是在elements數組的“活動”部分以外的任何引用都是過期的,活動部分是指elements中下標小于size的那些元素。
不如我們進行簡單測試:
public class Test { public static Stack stack = new Stack(); public static void main(String[] args) { try { for (int i = 0; i <= 5; i++) { String str = "effective " + i; stack.push(str); } String a = new String((String) stack.pop()); System.out.println(a); System.out.println((String) stack.elements[5]); } catch (Exception e) { e.printStackTrace(); } }}我們入棧了0~5共六條string,然后出棧,打印如下:
effective 5effective 5理所當然,打印a就是這樣,但是5已經出棧,第二次打印就不正常了。此時,要對Stack類進行修改:
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result;}再進行同樣的測試:
effective 5null例子中,只要一個單元被彈出棧,指向他的引用就已經過期,經過清空引用就可以避免后期的錯誤了。
那么,我們應該何時清空引用呢?一般而言,只要類是自己管理內存,像Stack類這樣,垃圾回收器并不知道數組的非活動區域已經變得不重要的時候,程序猿就應該警惕內存泄漏問題。
新聞熱點
疑難解答