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.