是否需要删除 Java 侦听器?(一般来说)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/157856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Do Java listeners need to be removed? (In general)
提问by Jeremy
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?
我担心我可能会在这里创建垃圾收集器问题。最佳做法是什么?
采纳答案by Jeremy
I just found a huge memory leak, so I am going to call the code that created the leak to be wrongand 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
}
回答by janm
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.
参考图中有一个循环。A 引用 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 不会被垃圾收集,因为存在对 AB 循环的外部引用。
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.
如果需要跨多个类可以进一步推广。
回答by workmad3
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)。
回答by Aaron
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 上的 finalize 调用,因为 A 中有 B 的实例,因此它永远不会被垃圾收集。您可以通过将 B 的引用存储在 A 中作为 WeakReference(在车库收集期间不被视为引用)来解决此问题,但是当您不再需要 B 时,最好从 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 方法,因为您永远无法确定它何时会被调用,并且您不能使用它来从另一个类中注销自己。
回答by entzik
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 中,您不会这样做。除非您真的知道自己在做什么,否则您不会覆盖 finalize。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。
回答by David Pierre
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 保持活动状态的静态内部类。
回答by Alexander
In your situation the only garbage collection "problem" is that instances of Bwon'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 Baren'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.
在您的情况下,唯一的垃圾收集“问题”是 的实例B不会被垃圾收集,而A. 这就是垃圾收集在 Java/.NET 中的工作方式。现在,如果您不喜欢 的实例B没有更早地被垃圾收集这一事实,您需要问问自己您希望它们在什么时候停止监听来自 的事件A?一旦你有了答案,你就会知道如何修复设计。
回答by Andre Bossard
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....
因为那样我真的很想知道那个匿名类会发生什么......
回答by Asaf R
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 传递给侦听器?
回答by pfranza
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>>();
A 确实会阻止 B 在您使用标准引用来存储您的侦听器时被垃圾收集。或者,当您维护侦听器列表而不是定义 new ArrayList<ListenerType>(); 你可以做一些类似 new ArrayList<WeakReference<ListenerType>>(); 的事情。
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
这当然只有在您编写包含侦听器的类时才有效

