Do Java listeners need to be removed? (In general)

java

Solution

I just found a huge memory leak, so I am going to call the code that created the leak to be wrong and my fix that does not leak as right.

Here is the old code: (This is a common pattern I have seen all over)

class Singleton {
    static Singleton getInstance() {...}
    void addListener(Listener listener) {...}
    void removeListener(Listener listener) {...}
}

class Leaky {
    Leaky() {
        // If the singleton changes the widget we need to know so register a listener
        Singleton singleton = Singleton.getInstance();
        singleton.addListener(new Listener() {
            void handleEvent() {
                doSomething();
            }
        });
    }
    void doSomething() {...}
}

// Elsewhere
while (1) {
    Leaky leaky = new Leaky();
    // ... do stuff
    // leaky falls out of scope
}

Clearly, this is bad. Many Leaky's are being created and never get garbage collected because the listeners keep them alive.

Here was my alternative that fixed my memory leak. This works because I only care about the event listener while the object exists. The listener should not keep the object alive.

class Singleton {
    static Singleton getInstance() {...}
    void addListener(Listener listener) {...}
    void removeListener(Listener listener) {...}
}

class NotLeaky {
    private NotLeakyListener listener;
    NotLeaky() {
        // If the singleton changes the widget we need to know so register a listener
        Singleton singleton = Singleton.getInstance();
        listener = new NotLeakyListener(this, singleton);
        singleton.addListener(listener);
    }
    void doSomething() {...}
    protected void finalize() {
        try {
            if (listener != null)
                listener.dispose();
        } finally {
            super.finalize();
        }
    }

    private static class NotLeakyListener implements Listener {
        private WeakReference<NotLeaky> ownerRef;
        private Singleton eventer;
        NotLeakyListener(NotLeaky owner, Singleton e) {
            ownerRef = new WeakReference<NotLeaky>(owner);
            eventer = e;
        }

        void dispose() {
            if (eventer != null) {
                eventer.removeListener(this);
                eventer = null;
            }
        }

        void handleEvent() {
            NotLeaky owner = ownerRef.get();
            if (owner == null) {
                dispose();
            } else {
                owner.doSomething();
            }
        }
    }
}

// Elsewhere
while (1) {
    NotLeaky notleaky = new NotLeaky();
    // ... do stuff
    // notleaky falls out of scope
}

Problem

Imagine this sample java class: ``` class A { void addListener(Listener obj); void removeListener(Listener obj); } class B { private A a; B() { a = new A(); a.addListener(new Listener() { void listen() {} } } ``` Do I need to add a finalize method to B to call a.removeListener? Assume that the A instance will be shared with some other objects as well and will outlive the B instance. I am worried that I might be creating a garbage collector problem here. What is the best practice?

Original source