Effective Java 第6条: 消除过期的对象引用

示例:
你找的出来下面代码中的内存泄漏(memory leak)吗

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_ININTIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_ININTIAL_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);
        }
    }
}

如果一个栈先增长,然后再收缩,那么pop出来的对象将不会被当做垃圾回收.
即使使用栈的程序不在引用这些对象,他们也不会被回收.
因为栈内部维护这这些对象的过期引用(永远不会再被解除的引用)

上面代码中,pop方法:

 public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        return elements[--size];
    }

这里将一个元素弹出后,引用仍被栈内部保存着,所以需要手动将其消除:

  public pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }

        Object result = elements[--size];
        elements[size] = null;  // Eliminate obsolete reference.
        return result;
    }

清除过期引用好处:

  • 避免内存泄漏
  • 避免错误处理引用, 会抛出NullPointException,而不是悄悄的继续运行,导致查不出bug.

清空对象引用应该是一种例外,而不是一种规范行为

消除过期引用的最好的方法:让包含该引用的变量结束在其生命周期.

Stack类自己管理内存.
一般而言,只要类是自己管理内存,程序员就应该警惕内存泄漏问题.一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空.

内存泄漏的另一个常见来源是缓存. 应该时不时清除缓存中没有用的项.(LinkedHashMap)

内存泄漏的第三个常见来源:监听器和其他回调. (只注册,未注销)

内存泄漏常见来源:

  • 过期引用
  • 缓存
  • 监听器和其他回调

你可能感兴趣的:(Effective Java 第6条: 消除过期的对象引用)