跟我学(Effective Java 2)第6条:消除过期的对象引用

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

入栈再弹栈,弹出的对象 即使没有被引用,也不会被回收,因为栈内部会维护这些对象的过期引用(obsolete reference)。

什么是过期引用呢?过期引用是指永远也不会被解除的引用,所有栈内活动部分之外的引用都是过期的,即被弹出后就是过期的。

内存泄露的第一种来源: 无意识对象保持。

在这里样例用手动清空的方式解决此问题,所以要进行有意识的清除此类对象。清空对象引用实则是一种例外,而不是常规使用,只有特殊情况下才使用。

优选让包含该引用的变量结束其生命周期。所以只要类是自己管理内存,程序员就应该警惕内存泄漏问题。一旦元素被释放掉,该元素中包含的任何对象引用都应该被清空。

/**
 * 消除过期的对象引用
 */
public class Stack {
    private Object[] elements;//如果该成员是final类型 则clone时会报错
    private int size = 0;
    private static final int DEFAULT_INITIAL = 16;

    public Stack(){
        elements = new Object[DEFAULT_INITIAL];
    }
    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }
    //弹栈的时候 出来的对象 及时没有被引用 也不会被垃圾回收 ,因为 栈内部会对这

些对象维护的过期引用存在内存泄露问题。
    //2 处理方法 清空被弹出的对象
    // 但是 方法依然不是最好的处理方法,最好的方法是调用该引用的对象或变量 结束

生命周期,本例就是Stack这个类结束生命周期时 自然会清空所有栈内对象。
    public Object pop(){
        if (size == 0){
            throw new EmptyStackException();
        }
        //return  elements[--size];
        //2
        Object result = elements[--size];
        elements[size] = null;//**看似简单的方式 却是解决内存泄露的重点。
        return result;
    }

    //扩容时 产生新的数组,引用指向新的 数组,但是 旧的对象依然存在没有只是没有

被引用
    private void ensureCapacity(){
        if (elements.length == size){
            elements = Arrays.copyOf(elements,2*size + 1);//扩容把旧的数组迁移到

新数组内
        }
    }

    @Override
    public Stack clone() {
        try {
            Stack result = (Stack)super.clone();//这属于表层复制,把目标对象基本

类型复制过来,如果有复杂的子域则需要二次复制
            result.elements = elements.clone();//克隆时 需要把被克隆对象的子域 

同时复制过来,尤其是容器类型。
            return result;
        }catch (CloneNotSupportedException ce){
            throw new AssertionError(":"+this.getClass());
        }
    }
}

内存泄露的第二种来源:缓存泄露。

一旦把对象的引用放入缓存中,它就很容易被忘掉,从而长时间不在用后仍然留在缓存中。只要在缓存之外存在对某个项的键的引用,该项就有意义;如果没有,可以使用WeakHashMap来代表缓存,过期则自动被删除。

对于更加复杂的缓存,则必须使用java.lang.ref 具体问题具体分析了。

内存泄露的第三种来源: 监听器和回调。

如果客户端在自己实现的API中注册回调,却没有显式地取消注册,那么除非自己采取某些动作,否则它们就会积聚。确保回调立即被当作垃圾回收的最佳方法是只保存它们的弱引用(weak reference),例如,只将它们保存为WeakHashMap中的键。

由于内存泄露不会出现明显的失败,所以只有通过仔细检查代码,或者借助于Heap剖析工具(Heap Profiler)才能发现内存泄露问题。所以要尽量提前做好设计和预测。

你可能感兴趣的:(跟我学(Effective,Java,2))