《TIJ4》多态 E13 继承与清理中的引用计数问题

引言

通常,我们无需考虑对象的清理问题,垃圾回收器会代劳。然而在特定情况下,需要手动控制对象的清理,这时,我们必须小心翼翼为类创建dispose()方法。由于继承的原因,如果子类中含有自己特殊的清理,那么子类就必须重写dispose()方法,并在执行完自己特殊的清理后调用父类的dispose()方法,否则,基类就不会清理。

一般的组合和继承中,对象会创建组合中的成员对象,并且在dispose()时释放这些对象。然而,如果这些成员对象中存在着被其他一个或多个对象所共享时,问题就复杂了,这时你不能简单得直接dispose(),你要考虑这些成员对象此时没有被其他对象共享,才能释放。下面是相关的代码:

package polymorphism;

class Shared {
    private int refcount = 0;
    private static long counter = 0;
    private final long id = counter++;

    public Shared() {
        System.out.println("Creating " + this);
    }

    public void addRef() {
        refcount++;
    }

    protected void dispose() {
        if (--refcount == 0) {
            System.out.println("Disposing " + this);
        }
    }

    @Override
    public String toString() {
        return "Shared " + id;
    }
}

class Composing {

    private Shared shared;
    private static long counter = 0;
    private final long id = counter++;

    public Composing(Shared shared) {
        System.out.println("Creating " + this);
        this.shared = shared;
        this.shared.addRef(); //通过某个 Shared 对象 构造一个 Composing 对象时,那么该shared 引用数加1
    }

    protected void dispose() {
        System.out.println("disposing " + this);
        shared.dispose();
    }

    @Override
    public String toString() {
        return "Composing " + id;
    }
}

public class ReferenceCounting {
    //存在被共享的成员对象时,共享对象使用引用计数方式进行dispose
    public static void main(String[] args) {
        Shared shared = new Shared(); //一个Shared对象被5个Composing对象共享
        Composing[] composings = {new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared)};
        for (Composing c : composings) {
            c.dispose();
        }
    }
}

/*输出:
Creating Shared 0
Creating Composing 0
Creating Composing 1
Creating Composing 2
Creating Composing 3
Creating Composing 4
disposing Composing 0
disposing Composing 1
disposing Composing 2
disposing Composing 3
disposing Composing 4
Disposing Shared 0
*/

注意,这里coutner是Shared的示例的数量,还可以为id提供数值,类型是long可以防止溢出,id是final的,因为我们不希望它的值在对象生命周期中被改变。
在将一个Shared对象附着在Composing上时,一定要调用addRef()来增加引用数量。
Shared对象在dispose()时将根据引用数量来决定是否清理。

问题

在Shared类中添加一个finalize()方法,用来校验终止条件。

添加的finalize()方法如下:

protected void finalize(){
    if(refcount != 0){
        System.out.println("Error: object is not properly cleaned-up!");
    }
}

很明显,终止条件就是refcount为0时。

而在main()方法最后添加如下代码:

new Composing(new Shared());
System.gc();
System.runFinalization();

新添加的这个Composing()对象没有进行释放(dispose),因此这个Shared()对象在被清理时不满足终结条件“引用数为0”。

注:不能这样声明:

Composing c = new Composing(new Shared());

或者

Shared s = new Shared();
new Composing(s);

这两种方式Shared对象会有引用指向,不会被垃圾回收。

posted @ 2018-04-12 11:26  庄浩  阅读(162)  评论(0编辑  收藏  举报