由于Java语言具有垃圾回收机制,可能很多人都不会再去关心内存管理的事情.但是有的情况下我们的代码中回无意的存在着 内存泄漏
的问题.下面的代码是一个例子:
public class Stack{
private Object[] elements;
private int size = 0;
private static final int DEAFULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEAFULT_INITIAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity(){
if(elements.length == size)
elements = Arrays.copyOf(elements,2*size+1);
}
}
上面的代码看似没有任何问题,但当栈先增长后收缩时,从栈中弹出的对象不会被系统回收,即使它们不再被引用.因为栈的内部会维护这些对象的 过期引用
,即被弹出的对象虽然不在栈内,但栈的内部( elements
中下标大于size的部分)仍然持有着他们的引用,只是用户不会访问这些对象而已.
当这些对象被无意识的保留起来时,垃圾回收机制不仅不会处理这些对象,而且也不会处理被这些对象所引用的其他所有对象.这样一来会对性能造成很大的影响.
处理这类问题其实也很简单,只要对象引用过期,就清空这些引用即可.对于上面的例子,我们可以在出栈方法进行下面的改进.
public Object pop(){
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 清除过期引用
return result;
}
那么问题来了,我们是否对于每个对象的引用,都要关心其是否已经过期?这肯定是不需要的,而且这也不是我们希望的.
只有当类是自己管理内存,我们才应该注意内存泄漏问题
.
内存泄漏还有另外的两个常见的情景,就是 缓存
以及 监听器和其他回调
.由于内存泄漏很难被发现,所以我们从编码的经验中能获取最有效的解决方法.
finalize方法通常不可预测,一般情况下不推荐使用.因为finalize方法会延迟执行,而延迟的时间是任意的,这会让程序的运行变得不可预测.