我正在阅读Effective Java第二版的“第6项:消除过时的对象引用”.

以下是代码段.

//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);
    }
}

根据这个项目,内存泄漏是因为在弹出之后,数组索引没有被引用为NULL,如下所示:

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

我的理解是假设对于给定的数组,我已经完成了元素[0] = new Object()然后我再次执行这个元素[0] = new Object()然后我的第一个对象将有资格进行垃圾收集,因为0th我的数组的索引不再指向它.


我的理解不正确吗?如果它是正确的,那么它如何在Effective Java中显示为内存泄漏.

解决方法:

你得到了大部分.

如果你这样做:

elements[0] = someOtherObject;

那么存储在索引0的另一个元素不再被引用并且可能被收集.

但是第一个pop()实现保留了该引用 – 它只减少了存储元素的“计数器”.因此,仍然引用该对象 – 并且在将新对象添加到堆栈之前不会收集该对象!

由于pop()的第二个版本中的注释明确指出 – 必须消除引用以确保堆栈不保留对该对象的引用.该对象应该被弹出 – 因此堆栈不应该保留有关该被删除对象的知识!

并确认提交:是的,当一个推送n个对象,然后推送n个其他对象,然后你没有内存泄漏 – 因为底层数组引用将全部更新并指向新对象.是的,如果弹出后推送的对象少于n个,则会保留过时的引用并阻止垃圾收集.

标签: java, memory, memory-management, garbage-collection, memory-leaks

相关文章推荐

添加新评论,含*的栏目为必填