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

There is a cycle in the reference graph. A references B and B references A. The garbage collector will detect cycles and see when there are no external references to A and B, and will then collect both.

Attempting to use the finaliser here is wrong. If B is being destroyed, the reference to A is also being removed.


The statement: “Assume that the A instance will be shared with some other objects as well and will outlive the B instance.” is wrong. The only way that will happen is if the listener is explicitly removed from somewhere other than a finalizer. If references to A are passed around, that will imply a reference to B, and B will not be garbage collected because there are external references to the A-B cycle.


Further update:

If you want to break the cycle and not require B to explicitly remove the listener, you can use a WeakReference. Something like this:

class A {
    void addListener(Listener obj);
    void removeListener(Listener obj);
}

class B {
    private static class InnerListener implements Listener {
        private WeakReference m_owner;
        private WeakReference m_source;

        InnerListener(B owner, A source) {
            m_owner = new WeakReference(owner);
            m_source = new WeakReference(source);
        }

        void listen() {
            // Handling reentrancy on this function left as an excercise.
            B b = (B)m_owner.get();
            if (b == null) {
                if (m_source != null) {
                    A a = (A) m_source.get();
                    if (a != null) {
                        a.removeListener(this);
                        m_source = null;
                    }
                }

                return;
            }
            ...
        }
    }

    private A a;

    B() {
        a = new A();
        a.addListener(new InnerListener(this, a));
    }
}

Could be further generalised if needed across multiple classes.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)