內存泄漏幾種常見的方式:
1. 無意識的對象保持。 就是接下來的例子。
2. 使用緩存。(很長一段時間仍然留在緩存中)
一旦你把對象引用放到緩存中,它就很容易被遺忘掉,從而使得它不再有用之后很長一段時間內仍然留在緩存中。對于這個問題,有幾種可能的解決方案。如果你正好要實現這樣的緩存:只要在緩存之外存在對某個項的鍵的引用,該項就有意義,那么就可以用WeakHashMap代表緩存;當緩存中的項過期之后,它們就會自動被刪除。記住只有當所要的緩存項的生命周期是由該鍵的外部引用而不是由值決定時,WeakHashMap才有用處。
更為常見的情形則是,"緩存項的生命周期是否有意義"并不是很容易確定,隨著時間的推移,其中的項會變得越來越沒有價值。在這種情況下,緩存應該時不時地清除掉沒用的項。這項清除工作可以由一個后臺線程(可能是Timer或者ScheduledThreadPoolExecutor)來完成,或者也可以在給緩存添加新條目的時候順便進行清理。LinkedHashMap類利用它的removeEldestEntry方法可以很容易地實現后一種方案。對于更加復雜的緩存,必須直接使用java.lang.ref。
3. 監聽器和其他回調
如果你在實現的是客戶端注冊回調卻沒有顯式地取消注冊的API,除非你采取某些動作,否則它們就會積聚。確保回調立即被當作垃圾回收的最佳方法是只保存它們的弱引用(weak
reference),例如,只將它們保存成WeakHashMap中的鍵。
對于 1.無意識的對象保持,代碼:
1 public class Stack {
2 private Object[] elements;
3 private int size = 0;
4 private static final int DEFAULT_INITIAL_CAPACITY = 16;
5
6 public Stack() {
7 elements = new Object[DEFAULT_INITIAL_CAPACITY];
8 }
9
10 public void push(Object e) {
11 ensureCapacity();
12 elements[size++] = e;
13 }
14
15 public Object pop() {
16 if (size == 0)
17 throw new EmptyStackException();
18 return elements[--size];
19 }
20
21 /**
22 * * Ensure space for at least one more element, roughly* doubling the
23 * capacity each time the array needs to grow.
24 */
25 private void ensureCapacity() {
26 if (elements.length == size)
27 elements = Arrays.copyOf(elements, 2 * size + 1);
28 }
29 }
修改方式:
把上面的pop方法修改成如下:
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
清空過期引用的另一個好處是,如果它們以后又被錯誤地解除引用,程序就會立即拋出NullPointerException異常,而不是悄悄地錯誤運行下去。盡快地檢測出程序中的錯誤總是有益的。