Effective Java的第6条中提到,以下程序可能会引起内存泄露(原文引用:随着垃圾回收器活动的增加,或者由于内存占用的不断增加,程序性能的降低会逐渐表现出来。在极端的情况下,这种内存泄漏会导致磁盘分页,甚至导致程序失败,而出现OutOfMemoryError错误,但是这种失败情形相对比较少见):
// 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);
}
}
泄露处在pop()函数中,--size后会导致大于size位置的elements依然被引用(即过期对象),从而不会被GC释放,解决方案是以下修改后的pop()函数:
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
我的问题是,原程序的pop()会有一些泄露,但是应该只是很小的部分,也不会连续泄露吧?
因为push()函数被调用的时候,会覆盖之前的原过期对象引用,那么原过期对象不就会被GC了么?
比如:
public void caller()
{
Stack stack = new Stack();
stack.push(objectA); //stack.elements[0]引用objectA, stack.size==1
stack.push(objectB); //stack.elements[1]引用objectB, stack.size==2
stack.push(objectC); //stack.elements[2]引用objectC, stack.size==3
stack.pop(); //stack.elements[2]依然引用到ObjectC,但是stack.size==2,导致文中提到的ObjectC成为过期对象,不能被GC
stack.pop(); //stack.elements[2]依然引用到ObjectB,但是stack.size==1,导致文中提到的ObjectB成为过期对象,不能被GC
stack.push(objectD); //stack.elements[1]引用objectD, stack.size==2,此时stack.elements[1]不再引用ObjectC,所以ObjectC应该可以被GC了吧?
stack.push(objecE); //stack.elements[2]引用objectE, stack.size==3, 此时stack.elements[2]不再引用ObjectD,所以ObjectD应该可以被GC了吧?
}
如上所述,我的迷惑是因为我对GC原理不是很清楚,在我看来push()被调用时,stack.elements[1]和stack.elements[2]已经被替换为ObjectD和ObjectE,所以ObjectB和ObjectC已经没被引用,可被GC了。
不知我的理解哪里有误,JAVA基础不好,还请多多指教。
------解决方案--------------------------------------------------------
------解决方案--------------------------------------------------------
这就是leak。
没有用的对象没有理由放在内存。
------解决方案--------------------------------------------------------
当然如果stack本身都没有被引用,我认为里面引用的对象也会被GC(至少应该被GC)