如何解决侦听器的优缺点作为WeakReferences
|| 将侦听器保留为WeakReferences的利弊是什么? 大的“专业版”当然是: 将侦听器添加为WeakReference意味着该侦听器不需要麻烦自己“删除”。 更新资料 对于那些担心侦听器仅引用该对象的人,为什么不能有2种方法addListener()和addWeakRefListener()? 那些不关心清除的人可以使用后者。解决方法
首先,在侦听器列表中使用WeakReference将为您的对象提供不同的语义,然后使用硬引用。在硬引用情况下,addListener(...)表示\“将特定事件通知给提供的对象,直到我用removeListener(..)明确停止它为止”;在弱引用情况下,它表示\“将有关特定事件的通知给对象特定事件,直到该对象将不会被其他任何人使用(或通过removeListener明确停止)\“。请注意,在许多情况下,拥有对象,侦听某些事件并且没有其他引用可将其与GC隔离是完全合法的。记录器可以是一个示例。
如您所见,使用WeakReference不仅可以解决一个问题(“我应该记住不要忘记在某个地方删除添加的监听器”),而且还会引发另一个问题-“我应该记住我的监听器可以停止请在没有参考的任何时候收听。”您不能解决问题,而只是将一个问题换成另一个。看,您被迫以任何一种方式明确定义,设计和跟踪收听者的寿命。
因此,就我个人而言,我同意提及,在侦听器列表中使用WeakReference更像是一种技巧,而不是解决方案。值得一提的是它的模式,例如,它有时可以为您提供帮助-使遗留代码正常工作。但这不是选择的模式:)
附言还应注意,WeakReference是什么引入了附加的间接级别,在某些情况下事件发生率极高时,间接级别可能会降低性能。
, 这不是一个完整的答案,但是您引用的优势也可能是其主要缺点。考虑一下,如果动作监听器实施得很弱会发生什么情况:
button.addActionListener(new ActionListener() {
// blah
});
该动作监听器随时会收集垃圾!对匿名类的唯一引用是您要向其添加事件的情况并不少见。
, 我已经看到大量的代码,其中未正确注销侦听器。这意味着它们仍然被不必要地调用来执行不必要的任务。
如果只有一个类依赖于侦听器,那么很容易清除,但是当25个类依赖于侦听器时会发生什么呢?正确注销它们变得更加棘手。事实是,您的代码可以从引用您的侦听器的一个对象开始,并在以后的版本中可以包含25个引用同一侦听器的对象。
不使用ѭ1等效于冒消耗不必要的内存和CPU的巨大风险。它更复杂,更棘手,并且需要在复杂代码中使用硬引用进行更多工作。
WeakReferences
充满优点,因为它们会自动清理。唯一的缺点是您一定不要忘记在代码的其他地方保留硬引用。通常,这将依赖于该侦听器。
我讨厌创建监听器的匿名类实例的代码(正如Kirk Woll提到的那样),因为一旦注册,就不能再注销这些监听器。您没有对它们的引用。恕我直言,这真的很糟糕。
当您不再需要对侦听器的引用时,也可以将其“ 3”引用。您无需再为此担心。
,确实没有专业人士。弱引用通常用于“可选”数据,例如您不想阻止垃圾收集的缓存。您不希望收集监听器垃圾,而是希望它继续监听。
更新:
好的,我想我可能已经知道您正在做什么。如果将短期侦听器添加到长期对象,则使用弱引用可能会有好处。因此,例如,如果您要向域对象添加PropertyChangeListeners以更新不断重新创建的GUI的状态,则域对象将保留在可能会建立的GUI上。考虑一个不断被重新创建的大型弹出对话框,其中一个监听器引用通过PropertyChangeListener返回到Employee对象。如果我错了,请纠正我,但是我认为整个PropertyChangeListener模式不再非常流行。
另一方面,如果您正在谈论GUI元素之间的侦听器或让域对象侦听GUI元素,那么您将不会购买任何东西,因为当GUI消失时,侦听器也会消失。
这是一些有趣的读物:
http://www.javalobby.org/java/forums/t19468.html
如何解决摆动侦听器内存泄漏?
, 坦白说,我并不是真的会接受这个想法,也不会真正希望您使用addWeakListener做些什么。也许只有我一个人,但这似乎是一个错误的好主意。起初它很诱人,但它可能暗示的问题却可以忽略不计。
使用weakReference,您不能确定在不再引用侦听器本身时是否将不再调用该侦听器。垃圾收集器可以在几毫秒后释放内存,或者永远不会释放。这意味着它可能会继续消耗CPU,并使诸如抛出异常之类的异常变得奇怪,因为不应调用侦听器。
swing的一个示例是尝试做只有在UI组件实际上已附加到活动窗口的情况下才能做的事情。这可能会引发异常,并影响通知程序,使其崩溃并阻止对有效的侦听器进行通知。
如前所述,第二个问题是匿名侦听器,它们可能过早被释放而从未通知过,也可能只有几次。
您试图实现的目标很危险,因为当您停止接收通知时,您将无法控制。它们可能会永远持续下去或停止得太早。
, 因为假设您要添加WeakReference侦听器,所以您正在使用自定义的Observable对象。
在以下情况下,对对象使用WeakReference是非常有意义的。
-可观察对象中有一个侦听器列表。
-您已经很难参考其他地方的听众。 (您必须对此有所确定)
-您不希望垃圾收集器仅因为Observable中有对它的引用而停止清除侦听器。
-在垃圾回收期间,将清除侦听器。在通知侦听器的方法中,您从通知列表中清除WeakReference对象。
, 我想不出将WeakReferences用于侦听器的任何合法用例,除非您的用例某种程度上涉及到下一个GC周期后明确不应该存在的侦听器(该用例当然是特定于VM /平台的) )。
可以为SoftReferences设想一个稍微合法的用例,在该用例中,侦听器是可选的,但会占用大量堆,并且在可用堆大小开始变小时应首先使用。我想,某种可选的缓存或其他类型的辅助侦听器可能是候选方法。即使那样,似乎您还是希望监听器的内部使用SoftReference,而不是监听器和被监听者之间的链接。
但是,通常,如果您使用的是持久性侦听器模式,则侦听器是非可选的,因此提出此问题可能是您需要重新考虑体系结构的症状。
这是学术问题,还是您要解决的实际情况?如果是实际情况,我很想听听它是什么,并且您可能会获得更多,更少的关于如何解决它的抽象建议。
,我认为在大多数情况下,这是个好主意。负责释放侦听器的代码在注册它的地方。
在实践中,我看到了很多软件可以永久保留收听者的声音。通常,程序员甚至都不知道应该注销它们。
通常有可能返回一个带有对侦听器的引用的自定义对象,该引用允许操纵何时注销。例如:
listeners.on(\"change\",new Runnable() {
public void run() {
System.out.println(\"hello!\");
}
}).keepFor(someInstance).keepFor(otherInstance);
此代码将注册侦听器,返回一个封装侦听器并具有方法的对象,keepFor会将侦听器添加到以实例参数为键的静态weakHashMap中。这将确保至少在不垃圾回收someInstance和otherInstance的情况下注册侦听器。
可能还有其他方法,例如keepForever()或keepUntilCalled(5)或keepUntil(DateTime.now()。plusSeconds(5))或unregisterNow()。
默认值可以永久保存(直到未注册)。
这也可以在没有弱引用但幻影引用会触发侦听器删除的情况下实现。
编辑:创建一个小库,实现该方法的基本版本https://github.com/creichlin/struwwel
, 我对原始海报有3条建议。很抱歉,您需要重新使用旧线程,但是我认为我的解决方案以前并未在该线程中讨论。
第一,
考虑遵循JavaFX库中的javafx.beans.values.WeakChangeListener示例。
第二,
我通过修改Observable的addListener方法来提升JavaFX模式。现在,新的addListener()方法为我创建了相应的WeakXxxListener类的实例。
可以轻松修改\“ fire event \”方法以取消引用XxxWeakListener并在WeakReference.get()返回null时将其删除。
由于我需要遍历整个列表,因此remove方法现在有点麻烦了,这意味着我需要进行同步。
第三,
在实施此策略之前,我采用了一种可能会有用的不同方法。 (硬引用)侦听器收到一个新事件,他们对是否仍在使用进行了现实检查。如果不是,则它们从观察者中退订,从而可以对其进行GC。对于寿命短的侦听器,如果订阅了寿命长的Observable,则检测过时是相当容易的。
为了尊重那些规定总是退订您的监听器的良好编程习惯,每当一个监听器诉诸取消订阅时,我确保创建一个日志条目,并在以后的代码中更正此问题。
, 如果您特别希望GC控制侦听器的生存期,则WeakListeners很有用。
如前所述,与通常的addListener / removeListener情况相比,这确实是不同的语义,但是在某些情况下有效。
例如,考虑一棵非常大的树,该树是稀疏的-某些级别的节点未明确定义,但可以从层次结构中更深的父节点推断出来。隐式定义的节点侦听已定义的那些父节点,以使它们的隐式/继承值保持最新。但是,树很大-我们不希望隐含节点永远存在-只要调用代码使用它们,再加上可能需要几秒钟的LRU缓存,以避免一遍又一遍地搅动相同的值。
在这里,弱监听器使子节点可以侦听父节点,同时其寿命也由可达性/缓存决定,因此该结构不会在内存中保留所有隐含节点。
, 如果您要在无法保证每次都调用的地方取消注册,则可能还需要使用WeakReference实现监听器。
我似乎还记得我们在ListView的行视图中使用的自定义PropertyChangeSupport
侦听器之一遇到了一些问题。我们找不到注销这些监听器的好方法和可靠的方法,因此使用WeakReference监听器似乎是最干净的解决方案。
, 从测试程序看来,匿名ActionListeners不会阻止对象被垃圾回收:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
public class ListenerGC {
private static ActionListener al = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.err.println(\"blah blah\");
}
};
public static void main(String[] args) throws InterruptedException {
{
NoisyButton sec = new NoisyButton(\"second\");
sec.addActionListener(al);
new NoisyButton(\"first\");
//sec.removeActionListener(al);
sec = null;
}
System.out.println(\"start collect\");
System.gc( );
System.out.println(\"end collect\");
Thread.sleep(1000);
System.out.println(\"end program\");
}
private static class NoisyButton extends JButton {
private static final long serialVersionUID = 1L;
private final String name;
public NoisyButton(String name) {
super();
this.name = name;
}
@Override
protected void finalize() throws Throwable {
System.out.println(name + \" finalized\");
super.finalize();
}
}
}
产生:
start collect
end collect
first finalized
second finalized
end program
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。