07.消除过期对象的引用

消除过期对象的引用

在java中,虽然JVM提供自动的垃圾回收机制,但是在某些时候我们还是需要手动将不再使用的对象置为null,删除对应的引用,从而让jvm回收该对象。

如下面的代码:

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

这里每次push时,会往数组中添加一个元素,在pop时会将数据索引 -1。这里使用一个指针记录数组的下标,从而模拟了一个简单的栈。

但是上面的代码存在一个问题,由于每次pop时只是修改了栈顶元素的索引,而没有修改或删除数组中的元素,因此这里数组还是引用了原有的对象。假设我们往栈中添加了1000个元素,随后弹出999个元素。从外部来看,此时栈中应该只剩下一个元素,但是实际上栈中依然存在1000个元素,只是通过栈顶指针对外只显示一个。

当大量的对象被放入栈中时,每一个对象都会被内部的数组引用,因此这里存在一处内存泄漏。

参考JDK中正确实现:

    public synchronized E pop() {
        E       obj;
        int     len = size();

        obj = peek();
        removeElementAt(len - 1);

        return obj;
    }

    public synchronized void removeElementAt(int index) {
        modCount++;
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }

这里可以看到,每次弹出一个元素后,会执行 elementData[elementCount] = null; 这个操作会将栈顶弹出的元素的数组引用置为null,从而让jvm在后面的GC中回收该对象。

类似文章