首页 > 代码库 > 第6条:消除过期对象的引用

第6条:消除过期对象的引用

让咱们先来看一下数组实现栈的例子:

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

但是,我们要知道将对象置为空只是一种手段,而不是规范。

消除引用的最好方法还是应该让包含改变量的引用结束其生命周期,最佳的方式就是在最紧凑的作用域范围内定义每一个变量。

 

———————————————————————————————————————————————————————————————————————————————

 

上面例子中内存泄露的最主要原因就是该类自己管理管理内存。