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中回收该对象。