让咱们先来看一下数组实现栈的例子:
package chaper1;import java.util.Arrays;
import java.util.EmptyStackException;public class Stack_Test00 {
private Object[] elements;
private static int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack_Test00(){
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];
}/**
* 每次栈中元素已满时,将栈的容量扩大一倍
*/
private void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements, 2 * size);
}
}
}
表面上看起这段代码并没有任何的错误之处,但是随着垃圾回收器活动的增加,或者由于占用内存的不断增加,程序性能的降低会逐渐表现出来。在极端情况下,这种内存泄露会导致磁盘交换(Disk
Paging),甚至导致程序失败(OutOfMemoryError错误),但是这种失败情形相对比较少见。
而这段内存代码内存泄露的主要原因在于,一个栈在先增长后收缩,那么战中pop出来的元素不会被当作垃圾回收,及时程序不再对其进行引用。
这是因为:栈内部维护着对这些对象的过期引用(obsolete
reference)。而所谓的过期引用就是指永远也不会被解除引用,不会解除引用则不能够被回收。
为了消除这种过期引用最好的方法就是再它无效的时候,将其置为null。
public Object pop(){
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
但是,我们要知道将对象置为空只是一种手段,而不是规范。
消除引用的最好方法还是应该让包含改变量的引用结束其生命周期,最佳的方式就是在最紧凑的作用域范围内定义每一个变量。
———————————————————————————————————————————————————————————————————————————————
上面例子中内存泄露的最主要原因就是该类自己管理管理内存。
第6条:消除过期对象的引用