第二章第6、7条经验
消除过期的对象引用
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 ){
ensureCapcity();
elements[size++] = e;
}
public Object pop(){
if (size == 0)
throw new EmptyStackException;
return elements[--size];
}
private void ensureCapcity(){
if(elements.length == size)
elements = Arrays.copyOf(elements,2*size+1);
}
}
以上代码存在内存泄漏,泄漏的原因是当一个栈先增长,再收缩是,由于堆栈的内部维护着对象的永远也不会被解除的引用,即 过期引用;
在上面的例子中,凡是在elements数组的“活动部分”之外的任何引用都是过期的。所谓“活动部分”是指elements中小于下标size的那些元素。其修复方法很简单,如下
public Object pop(){
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; //清空对象引用
return result;
}
清空对象引用应该是一个例外,而不是一种规范行为;
什么时候应该清空清空对象引用?只要类是自己管理内存,就应该警惕内存泄漏问题,一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。
第二种内存泄漏来源:缓存。可以使用WeakHashMap,但只有当所要缓存的项的生命周期是由该键的外部引用而不是由直决定时,WeakHashMap才有用处。另外也可以用后端线程来完成。对于更加复杂的场景使用java.lang.ref。
第三种内存泄漏来源:监听器和其他回调。例如你写的API,客户端在这个api中注册回调,却没有显式的取消注册,除非采取某些动作,否则就会它们就会积聚。最佳方法:只保存他们的弱引用,如:只将它们保存成WeakHashMap中的键。
以上三种内存泄露的共同点是,实质上是由程序员来进行管理内存,客观上使java垃圾回收机制失效。
弱引用,在java中是指如果一个对象没有只有弱引用的情况下,是会被垃圾回收其回收的引用。
对于WeakHashMap的使用场景请查看下面的链接:
避免使用终结方法(finalizer)
终结方法通常是不可预测的,也是很危险的,一般情况下是不必要的。
终结方法的缺点:
- 不能保证会被及时的执行。
- 对移植性的影响:及时地执行终结方法正式垃圾回收算法的一个主要功能,但这种算法在不同JVM中的实现大相径庭。一个程序在你测试用的jvm平台上运行良好,而在你最重要顾客的jvm平台上却根本无法运行,这是完全有可能的。
- 不能保证被执行,不应依赖终结方法来更新重要的持久状态。
- 在终结方法中异常会被忽略,甚至连警告都不会打印出来。
- 使用终结方法有一个非常严重的性能损失。
如果确实需要终止某些资源,只需提供一个显式的终止方法,并要求该类的客户端在实例不再使用的时候调用这个方法。要注意的是:该实例必须记录下自己是否已经被终止了,如果在被终止之后被调用,就需要抛出IllegalStateException异常。
显示终止方法,通常与try-finally结构结合来使用,以确保及时终止。
终结方法的好处:
有两种用途,一种是充当安全网,防止客户端忘记调用显示的终止方法。虽然资源不会被及时的关闭,但迟关总比永远不关强。如果终结方法发现资源还未被终止,则应该记录条警告日志。