Item 6: Eliminate obsolete object references

虽然Java有垃圾回收功能,但有时候还是要手动回收过期对象引用。

内存泄露第一个常见来源

比如,下面这段代码存在内存泄露。

// Can you spot the "memory leak"?
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);
    }
}

So where is the memory leak? If a stack grows and then shrinks, the objects
that were popped off the stack will not be garbage collected, even if the program
using the stack has no more references to them.
一个堆栈push之后又pop,增大后又收缩,这样弹出的object不会被垃圾回收。
正确做法:

public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}

这有点颠覆我的观念了。因为我很少有人用完array之后把无效的元素赋值成null的。
比如DFS中常用的backtracking常常会缩小array:

stringBuilder.deleteCharAt(sb.length() - 1);
//或者
arrayList.remove(arrayList.size() - 1);

但从没见过有人会把弹出的那个最后的元素解除引用啊。。
于是书里又说,Nulling out object references should be the exception rather than the norm. 清空引用是例外而不是常态。
对GC来说数组中所有对象引用都有效,但我们知道size之外的已经没用了所以要手动回收。

我在网上看到一个例子,如何手写一个例子让编译器提示outOfMemory?这样做:

public class OutOfMemoryTest {
    public static void main(String[] args){
        List list=new ArrayList();
        for(;;){
            int[] tmp=new int[1000000];
            list.add(tmp);
        }
    }
}

另外提一下,

  1. Arrays.copyOf会截取或者扩大一个array。扩大的新array后面会填充null。
  2. main函数的String args[] 的args就像命令行中加入的额外参数。

内存泄露第二个常见来源

缓存。放到缓存中的对象引用容易被遗忘。可以用WeakHashMap。

内存泄露第三个常见来源

监听器和其他回调。解决方法是用WeakReference。
这个我记得很久以前写过register和deregister接口,但忘了啥意思了。。

你可能感兴趣的:(Item 6: Eliminate obsolete object references)