是否需要删除Java侦听器? (一般来说)

时间:2021-01-18 00:02:51

Imagine this sample java class:

想象一下这个示例java类:

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.

我是否需要向B添加finalize方法以调用a.removeListener?假设A实例也将与其他一些对象共享,并且将比B实例更长。

I am worried that I might be creating a garbage collector problem here. What is the best practice?

我担心我可能会在这里创建一个垃圾收集器问题。什么是最佳做法?

12 个解决方案

#1


17  

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.

参考图中有一个循环。引用B和B引用A.垃圾收集器将检测周期并查看何时没有对A和B的外部引用,然后将收集两者。

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

试图在这里使用终结器是错误的。如果B被销毁,则对A的引用也将被删除。


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.

声明:“假设A实例也将与其他一些对象共享,并且将比B实例更长。”是错的。唯一的方法是如果从终结器以外的某个地方明确地移除了侦听器。如果传递对A的引用,那将意味着对B的引用,并且B将不会被垃圾收集,因为存在对A-B循环的外部引用。


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:

如果要中断循环并且不要求B显式删除侦听器,则可以使用WeakReference。像这样的东西:

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.

如果需要跨多个类可以进一步推广。

#2


5  

My understanding of the GC is that, until the removeListener method is called, class A will be maintaining a reference to the listener and so it won't be a candidate for GC cleanup (and hence finalize won't be called).

我对GC的理解是,在调用removeListener方法之前,类A将维护对侦听器的引用,因此它不会成为GC清理的候选者(因此不会调用finalize)。

#3


3  

If you have added B as a listener to A, and A is meant to outlive B, the finalize call on B will never get called because there is an instance of B inside of A, so it will never get garbage collected. You could get around this by storing a reference to B in A as a WeakReference (which is not considered a reference during garage collection), but it would be better to explicitly deregister B from A when you no longer need it.

如果您已将B添加为A的侦听器,并且A意味着比B更长,则B上的最终调用将永远不会被调用,因为在A内部存在B的实例,因此它将永远不会被垃圾收集。您可以通过在A中存储对B的引用作为WeakReference(在车库收集期间不被视为参考)来解决这个问题,但是当您不再需要时,最好从A中明确注销B.

In general it is advised in Java to not use the finalize method in Java because you can never be sure when it will be called, and you can not use it to deregister yourself from another class.

一般来说,在Java中建议不要在Java中使用finalize方法,因为你永远无法确定它何时被调用,并且你不能使用它从另一个类中注销自己。

#4


3  

You must be coming from C++ or some other language where people implement destructors. In Java you don't do that. You don't override finalize unless you really know what you're doing. In 10 years I never had to do that, and I still can't think of a good reason that would require me to do it.

您必须来自C ++或人们实现析构函数的其他语言。在Java中,你不这样做。除非你真的知道自己在做什么,否则不要覆盖终结。在10年里,我从来没有这样做,我仍然想不出一个需要我去做的好理由。

Back to your question, your listener is an independent object with its own life cycle and will collected after all other objects that reference it will be collected or when no other object will be pointing to it. This works very well. So no, you don't have to override finalize.

回到你的问题,你的监听器是一个独立的对象,它有自己的生命周期,并且会在收集所有引用它的其他对象之后或者没有其他对象指向它时收集。这非常有效。所以不,你不必覆盖finalize。

#5


2  

A will indeed keep B alive through the anonymous instance.

A确实会通过匿名实例保持B存活。

But I wouldn't override finalize to address that, rather use a static inner class who doesn't keep the B alive.

但我不会覆盖finalize来解决这个问题,而是使用一个静态的内部类,它不会让B保持活着。

#6


2  

In your situation the only garbage collection "problem" is that instances of B won't be garbage collected while there are hard-references to the shared instance of A. This is how garbage collection supposed to work in Java/.NET. Now, if you don't like the fact that instances of B aren't garbage-collected earlier, you need to ask yourself at what point you want them to stop listening to events from A? Once you have the answer, you'll know how to fix the design.

在您的情况下,唯一的垃圾收集“问题”是,当存在对A的共享实例的硬引用时,B的实例不会被垃圾收集。这就是垃圾收集应该在Java / .NET中工作的方式。现在,如果你不喜欢B实例之前没有垃圾收集的事实,你需要问自己,你希望他们在什么时候停止收听来自A的事件?一旦得到答案,您就会知道如何修复设计。

#7


1  

A holds a reference to B through the anonymous instance in implicitly used by the anonymous type created. This means B won't be freed until removeListener is called, and thus B's finalize won't be called.

A通过创建的匿名类型隐式使用的匿名实例保存对B的引用。这意味着在调用removeListener之前不会释放B,因此不会调用B的finalize。

When A is destroyed, it's anonymous reference to B will also B destroyed opening the way to B being freed.

当A被摧毁时,它对B的匿名引用也会破坏B打开通往B的方式。

But since B holds a reference to A this never happens. This seems like a design issue - if A has a calls a listener, why do you need B to also hold a reference to A? Why not pass the A that made the call to the listener, if necessary?

但由于B持有对A的引用,因此这种情况从未发生过。这似乎是一个设计问题 - 如果A有一个监听器,你为什么还需要B来保持对A的引用?如果有必要,为什么不将调用的A传递给听众呢?

#8


1  

How can A outlive B?:

A如何比B更活跃?:

Example Usage of B and A:

B和A的使用示例:

public static main(args) {
    B myB = new B();
    myB = null;
}

Behaviour I'd expect:

我期望的行为:

GC will remove myB and in the myB instance was to only reference to the A instance, so it will be removed too. With all their assigned listeners?

GC将删除myB,并且在myB实例中仅引用A实例,因此它也将被删除。与他们分配的所有听众?

Did you maybe mean:

你的意思是:

class B {
    private A a;
    B(A a) {
        this.a = a;
        a.addListener(new Listener() {
            void listen() {}
        }
}

With usage:

public static main(args) {
    A myA = new A();
    B myB = new B(myA);
    myB = null;
}

Because then I would really wonder what happens to that anonymous class....

因为那时我真的很想知道那个匿名课程会发生什么......

#9


0  

When the B is garbage collected it should allow the A to be garbage collected as well, and therefore any references in A as well. You don't need to explicitly remove the references in A.

当B被垃圾收集时,它应该允许A被垃圾收集,因此也是A中的任何引用。您无需在A中显式删除引用。

I don't know of any data on whether what you suggest would make the garbage collector run more efficiently, however, and when it's worth the bother, but I'd be interested in seeing it.

我不知道你的建议是否会使垃圾收集器更有效地运行的任何数据,当它值得麻烦时,但我有兴趣看到它。

#10


0  

Building on what @Alexander said about removing yourself as a listener:

基于@Alexander关于将自己作为听众去除的说法:

Unless there is some compelling reason not to, one thing I've learned from my co-workers is that instead of making an anonymous inner Listener, and needing to store it in a variable, make B implement Listener, and then B can remove itself when it needs to with a.removeListener(this)

除非有一些令人信服的理由不这样做,我从同事那里学到的一件事是,不是制作一个匿名的内部监听器,而是需要将它存储在变量中,使B实现监听器,然后B可以自行移除何时需要使用a.removeListener(this)

#11


0  

A will indeed keep B from being garbage collected in you are using standard references to store your listeners. Alternatively when you are maintaining lists of listeners instead of defining new ArrayList<ListenerType>(); you could do something like new ArrayList<WeakReference<ListenerType>>();

在您使用标准引用存储您的侦听器时,确实会使B不被垃圾收集。或者,当您维护侦听器列表而不是定义新的ArrayList ()时;你可以做一些像新的ArrayList >();

By wrapping your object in a weakReference you can keep it from prolonging the life of the object.

通过将对象包装在weakReference中,可以防止它延长对象的使用寿命。

This only works of course if you are writing the class that holds the listeners

当然,如果您正在编写包含侦听器的类,这当然有效

#12


0  

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.

显然,这很糟糕。许多Leaky正在被创建,并且永远不会收集垃圾,因为听众会让他们活着。

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
}

#1


17  

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.

参考图中有一个循环。引用B和B引用A.垃圾收集器将检测周期并查看何时没有对A和B的外部引用,然后将收集两者。

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

试图在这里使用终结器是错误的。如果B被销毁,则对A的引用也将被删除。


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.

声明:“假设A实例也将与其他一些对象共享,并且将比B实例更长。”是错的。唯一的方法是如果从终结器以外的某个地方明确地移除了侦听器。如果传递对A的引用,那将意味着对B的引用,并且B将不会被垃圾收集,因为存在对A-B循环的外部引用。


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:

如果要中断循环并且不要求B显式删除侦听器,则可以使用WeakReference。像这样的东西:

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.

如果需要跨多个类可以进一步推广。

#2


5  

My understanding of the GC is that, until the removeListener method is called, class A will be maintaining a reference to the listener and so it won't be a candidate for GC cleanup (and hence finalize won't be called).

我对GC的理解是,在调用removeListener方法之前,类A将维护对侦听器的引用,因此它不会成为GC清理的候选者(因此不会调用finalize)。

#3


3  

If you have added B as a listener to A, and A is meant to outlive B, the finalize call on B will never get called because there is an instance of B inside of A, so it will never get garbage collected. You could get around this by storing a reference to B in A as a WeakReference (which is not considered a reference during garage collection), but it would be better to explicitly deregister B from A when you no longer need it.

如果您已将B添加为A的侦听器,并且A意味着比B更长,则B上的最终调用将永远不会被调用,因为在A内部存在B的实例,因此它将永远不会被垃圾收集。您可以通过在A中存储对B的引用作为WeakReference(在车库收集期间不被视为参考)来解决这个问题,但是当您不再需要时,最好从A中明确注销B.

In general it is advised in Java to not use the finalize method in Java because you can never be sure when it will be called, and you can not use it to deregister yourself from another class.

一般来说,在Java中建议不要在Java中使用finalize方法,因为你永远无法确定它何时被调用,并且你不能使用它从另一个类中注销自己。

#4


3  

You must be coming from C++ or some other language where people implement destructors. In Java you don't do that. You don't override finalize unless you really know what you're doing. In 10 years I never had to do that, and I still can't think of a good reason that would require me to do it.

您必须来自C ++或人们实现析构函数的其他语言。在Java中,你不这样做。除非你真的知道自己在做什么,否则不要覆盖终结。在10年里,我从来没有这样做,我仍然想不出一个需要我去做的好理由。

Back to your question, your listener is an independent object with its own life cycle and will collected after all other objects that reference it will be collected or when no other object will be pointing to it. This works very well. So no, you don't have to override finalize.

回到你的问题,你的监听器是一个独立的对象,它有自己的生命周期,并且会在收集所有引用它的其他对象之后或者没有其他对象指向它时收集。这非常有效。所以不,你不必覆盖finalize。

#5


2  

A will indeed keep B alive through the anonymous instance.

A确实会通过匿名实例保持B存活。

But I wouldn't override finalize to address that, rather use a static inner class who doesn't keep the B alive.

但我不会覆盖finalize来解决这个问题,而是使用一个静态的内部类,它不会让B保持活着。

#6


2  

In your situation the only garbage collection "problem" is that instances of B won't be garbage collected while there are hard-references to the shared instance of A. This is how garbage collection supposed to work in Java/.NET. Now, if you don't like the fact that instances of B aren't garbage-collected earlier, you need to ask yourself at what point you want them to stop listening to events from A? Once you have the answer, you'll know how to fix the design.

在您的情况下,唯一的垃圾收集“问题”是,当存在对A的共享实例的硬引用时,B的实例不会被垃圾收集。这就是垃圾收集应该在Java / .NET中工作的方式。现在,如果你不喜欢B实例之前没有垃圾收集的事实,你需要问自己,你希望他们在什么时候停止收听来自A的事件?一旦得到答案,您就会知道如何修复设计。

#7


1  

A holds a reference to B through the anonymous instance in implicitly used by the anonymous type created. This means B won't be freed until removeListener is called, and thus B's finalize won't be called.

A通过创建的匿名类型隐式使用的匿名实例保存对B的引用。这意味着在调用removeListener之前不会释放B,因此不会调用B的finalize。

When A is destroyed, it's anonymous reference to B will also B destroyed opening the way to B being freed.

当A被摧毁时,它对B的匿名引用也会破坏B打开通往B的方式。

But since B holds a reference to A this never happens. This seems like a design issue - if A has a calls a listener, why do you need B to also hold a reference to A? Why not pass the A that made the call to the listener, if necessary?

但由于B持有对A的引用,因此这种情况从未发生过。这似乎是一个设计问题 - 如果A有一个监听器,你为什么还需要B来保持对A的引用?如果有必要,为什么不将调用的A传递给听众呢?

#8


1  

How can A outlive B?:

A如何比B更活跃?:

Example Usage of B and A:

B和A的使用示例:

public static main(args) {
    B myB = new B();
    myB = null;
}

Behaviour I'd expect:

我期望的行为:

GC will remove myB and in the myB instance was to only reference to the A instance, so it will be removed too. With all their assigned listeners?

GC将删除myB,并且在myB实例中仅引用A实例,因此它也将被删除。与他们分配的所有听众?

Did you maybe mean:

你的意思是:

class B {
    private A a;
    B(A a) {
        this.a = a;
        a.addListener(new Listener() {
            void listen() {}
        }
}

With usage:

public static main(args) {
    A myA = new A();
    B myB = new B(myA);
    myB = null;
}

Because then I would really wonder what happens to that anonymous class....

因为那时我真的很想知道那个匿名课程会发生什么......

#9


0  

When the B is garbage collected it should allow the A to be garbage collected as well, and therefore any references in A as well. You don't need to explicitly remove the references in A.

当B被垃圾收集时,它应该允许A被垃圾收集,因此也是A中的任何引用。您无需在A中显式删除引用。

I don't know of any data on whether what you suggest would make the garbage collector run more efficiently, however, and when it's worth the bother, but I'd be interested in seeing it.

我不知道你的建议是否会使垃圾收集器更有效地运行的任何数据,当它值得麻烦时,但我有兴趣看到它。

#10


0  

Building on what @Alexander said about removing yourself as a listener:

基于@Alexander关于将自己作为听众去除的说法:

Unless there is some compelling reason not to, one thing I've learned from my co-workers is that instead of making an anonymous inner Listener, and needing to store it in a variable, make B implement Listener, and then B can remove itself when it needs to with a.removeListener(this)

除非有一些令人信服的理由不这样做,我从同事那里学到的一件事是,不是制作一个匿名的内部监听器,而是需要将它存储在变量中,使B实现监听器,然后B可以自行移除何时需要使用a.removeListener(this)

#11


0  

A will indeed keep B from being garbage collected in you are using standard references to store your listeners. Alternatively when you are maintaining lists of listeners instead of defining new ArrayList<ListenerType>(); you could do something like new ArrayList<WeakReference<ListenerType>>();

在您使用标准引用存储您的侦听器时,确实会使B不被垃圾收集。或者,当您维护侦听器列表而不是定义新的ArrayList ()时;你可以做一些像新的ArrayList >();

By wrapping your object in a weakReference you can keep it from prolonging the life of the object.

通过将对象包装在weakReference中,可以防止它延长对象的使用寿命。

This only works of course if you are writing the class that holds the listeners

当然,如果您正在编写包含侦听器的类,这当然有效

#12


0  

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.

显然,这很糟糕。许多Leaky正在被创建,并且永远不会收集垃圾,因为听众会让他们活着。

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
}