第5条:消除过期的对象引用
考虑下面的代码:
如果一个栈先增长然后再缩小,那么从战栈中弹出来的对象并不会被回收,即使使用这个栈的程序不再引用它们.这是因为栈内部维护者对这些对象的过期引用。所谓的过期引用
是指永远也不会再被解除的引用。这种情况可能会导致内存泄露。
要想修复这一问题也很简单:一旦对象引用已经过期,只需清空这些引用即可。例如上例中只要一个单元被弹出那么指向它的引用就过期了,pop方法修正后如下:
清除过期引用的另一个好处是,如果以后它们又被错误的接触引用,则程序会立即抛出空指针异常,而不是悄悄的错误运行下去。
1、不必过分小心,例如对每一个对象引用一旦程序不用就立即清空。这样会使代码很乱同时降低程序效率。
2、一般而言,只要一个类自己管理它的内存,程序员就要警惕内存泄露的问题。
3、内存泄露的另一个常见来源是缓存。
考虑下面的代码:
// Can you spot the "memory leak"?
public class Stack {
private Object[] elements;
private int size = 0;
public Stack(int initialCapacity) {
this.elements = new Object[initialCapacity];
}
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) {
Object[] oldElements = elements;
elements = new Object[2 * elements.length + 1];
System.arraycopy(oldElements, 0, elements, 0, size);
}
}
}
如果一个栈先增长然后再缩小,那么从战栈中弹出来的对象并不会被回收,即使使用这个栈的程序不再引用它们.这是因为栈内部维护者对这些对象的过期引用。所谓的过期引用
是指永远也不会再被解除的引用。这种情况可能会导致内存泄露。
要想修复这一问题也很简单:一旦对象引用已经过期,只需清空这些引用即可。例如上例中只要一个单元被弹出那么指向它的引用就过期了,pop方法修正后如下:
public Object pop() {
if (size==0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
清除过期引用的另一个好处是,如果以后它们又被错误的接触引用,则程序会立即抛出空指针异常,而不是悄悄的错误运行下去。
1、不必过分小心,例如对每一个对象引用一旦程序不用就立即清空。这样会使代码很乱同时降低程序效率。
2、一般而言,只要一个类自己管理它的内存,程序员就要警惕内存泄露的问题。
3、内存泄露的另一个常见来源是缓存。
关于java中的几种引用关系,参考:Java基础 之软引用、弱引用、虚引用
第6条:避免使用才终结函数
1、终结函数(finalizer)通常是不可预测的,危险的,一般情况也是不必要的。
2、终结函数会导致不稳定的行为、更差的性能和移植性。
3、由于终结函数不能确保被即使的执行,所以对于时间关键的任务不应该使用终结函数来完成。例如:关闭已打开的文件。
4、我们不应该依赖终结函数来更新关键性的永久状态.例如:释放共享资源(比如数据库)上的永久锁。
但有两种情况下可以使用
1.当对象所有者忘记调用显示的终结方法(如connection,inputstream,outputstream的close方法和Timer中的cancel方法,这只是一种保险的方法
完全可以由程序员的细心避免的,所以使用这些类的时候千万要记住调用显示的终结方法