转载

Effective Java学习系列 --- 创建与销毁对象03

由于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;
}

那么问题来了,我们是否对于每个对象的引用,都要关心其是否已经过期?这肯定是不需要的,而且这也不是我们希望的. 只有当类是自己管理内存,我们才应该注意内存泄漏问题 .

内存泄漏还有另外的两个常见的情景,就是 缓存 以及 监听器和其他回调 .由于内存泄漏很难被发现,所以我们从编码的经验中能获取最有效的解决方法.

避免使用finalizer

finalize方法通常不可预测,一般情况下不推荐使用.因为finalize方法会延迟执行,而延迟的时间是任意的,这会让程序的运行变得不可预测.

原文  http://kevinhqf.github.io/2016/08/14/effective-java02/
正文到此结束
Loading...