我有以下代码片段:
android:windowSoftInputMode="adjustPan|adjustResize"
Theres是匿名public class Example {
private Integer threshold;
private Map<String, Progress> history;
protected void activate(ComponentContext ctx) {
this.history = Collections.synchronizedMap(new LinkedHashMap<String, Progress>() {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Progress> entry) {
return size() > threshold;
}
});
}
}
类和LinkedHashMap
类之间的循环依赖关系。这样可以吗?为什么不?它会被垃圾收集器很好地回收吗?
答案 0 :(得分:22)
这样可以吗?
这完全没问题。
threshold
是一个字段,因此可以在匿名类中引用它而没有任何问题。 (如果threshold
是一个局部变量,它必须(有效地)最终。)
类之间的循环依赖关系是常见的,当依赖关系图很小时(如本例所示),它不会造成任何问题。您LinkedHashMap
是匿名课程的事实在这里并不重要。
它会被垃圾收集器很好地回收吗?
关于内存泄漏+内部类的唯一警惕是(非静态)内部类具有对其封闭对象的隐式引用。这意味着如果你创建了很多很多内部类的实例,你就不能期望外部类对象的实例被垃圾收集。
在这种情况下,这意味着如果您泄漏对history
地图的引用,Example
的实例将不会被GC修改。
相关说明:
考虑到您正在使用synchronizedMap
,您似乎正在研究多线程程序。如果是这种情况,您需要警惕threshold
字段的同步和可见性问题。
如果可能,请尝试将threshold
字段设为最终
另一种选择是为LinkedHashMap
创建一个命名类,并将threshold
包含在该类中作为字段。
答案 1 :(得分:6)
无论如何,你都有这种依赖,因为匿名内部类的每个对象都隐含了对封闭类对象的引用。 Java就是这样设计的,嵌套的内部类有这个参考是有原因的,因此从语言规范的角度来看,这会编译并看起来非常正常。
关于(缺少)&#34;设计气味&#34;,如果这个匿名类对象完全封装在Example
类中,没有独特的意义没有其封闭的上下文,并且不会泄露到外面的任何地方对于Example
类,引用封闭类的字段没有任何问题。您只需使用此内部类对某些逻辑进行分组。
但是,如果此对象从封闭对象中泄漏(例如,通过getter返回),则应禁止此操作或将其重构为接收threshold
作为参数的静态内部类。此内部对象保存对封闭对象的引用,并可能使其远离GC,从而导致内存泄漏。
答案 2 :(得分:2)
循环依赖本身并不坏,但它可能会导致一些意外的内存泄漏。
按原样拍摄你的例子,它现在很好,因为它可以做你想做的事。
但是,如果您或其他人修改您的代码以公开您的私人代码:
private Map<String, Progress> history;
然后你可能有麻烦。会发生什么事情,你也会传递对Example类的引用,无论是否有意,因为你的内部类有隐式引用它。
我现在不能给你直接引用,但Steve McConnell在他的代码中完成了将循环依赖项称为反模式。你可以在那里阅读或者我猜谷歌,非常详细地阅读。
我能想到的另一个问题是,循环依赖相当难以进行单元测试,因为您在对象之间创建了非常高级别的耦合。
一般情况下,你应该避免循环依赖,除非你有充分的理由不这样做,比如实现循环链表。
答案 3 :(得分:2)
每当你实例化一个非静态的内部类(无论是命名还是匿名)时,这个内部类都会出现。 instance自动获取对封闭父类实例的引用。
以上意味着如果外部类也包含对非静态内部类的引用(如代码中的情况),则外部类的实例与非静态内部类之间存在循环依赖关系(再次,命名和匿名)。
此设置中唯一的实际问题是您对此现有交叉引用的使用是否合法。在您的特定情况下,我没有看到任何问题 - 非静态内部类使用封闭外部类的实例变量。对我来说似乎是犹太人。
在这种情况下,内部泄漏通常发生在对内部类的实例的引用传递到外部类之外时(通常是各种Listeners
的情况) - 因为此实例具有对外部类的实例,外部类不能被垃圾收集。但是,如果您只是交叉引用外部类和内部类,我不会认为会导致内存泄漏 - 它们将被垃圾收集在一起。
答案 4 :(得分:1)
我不喜欢你的解决方案(即使我同意这可行):
您的类示例应该实现Map或扩展LinkedHashMap,因为在那里定义了实例变量阈值并使用自己的定义优化了LinkedHashMap的概念。
您的类示例不应实现Map或扩展LinkedHashMap,因为activate方法不会优化LinkedHashMap或Map,而是使用Maps的概念。
1 + 2 =&gt;受孕问题。