消除过期的对象引用
java虽然有垃圾回收器,但是不是说在任何情况下都不用考虑内存泄露的问题了。
下面例子是一个简单的栈,包括入栈出栈。
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];
}
//确保栈数组容量
public void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements,2*size+1);
}
}
}
这个栈方法里面看似没有任何问题,可以实现入栈出栈操作,但是仔细琢磨就会发现其中存在着内存泄露。
从程序中可以看出,一个栈如果先增长再收缩,从栈中弹出的对象即使不使用了,但是还是会有过期引用导致垃圾收集器不会回收他们,过期引用就是永远不会被回收的引用。
例如一个栈中现在有八个元素。栈的长度是16
先增长:往里面push了四个元素,现在成了12个。
再收缩:然后现在又pop了两个元素,现在成了10个元素。
对于程序来说,现在我们只需要栈中的10个元素就好了,其余的都应该被垃圾回收掉,但是刚才收缩pop了的两个元素,确还是有过期引用存在。即栈数组下边为10,11还是有对应的对象存在。这样子就导致了内存泄露。如果执行了一个递归入栈,没有及时清理过期引用,那么后果是不堪设想的。
想要避免这种情况发生,那么只需要清理过期引用就可以了,即栈中有一个元素被弹出,那么就清空引用就行了。
修改后的出栈如下所示。
这样子即使程序对过期的引用错误的使用了,也能及时的抛空指针异常,而不是错误的运行下去了。
//出栈
public Object pop() {
if (size == 0) throw new EmptyStackException();
Object element = elements[--size];
//每弹出一个,就将栈该弹出下边的引用修改为null
elements[size] = null;
return element;
}