我知道弱引用受垃圾收集器的摆布,我们不能保证弱引用会存在。我看不出有必要有弱参考,但肯定应该有一个原因。
- 为什么我们在 Java 中需要弱引用?
- 弱引用在java中的实际(一些)用途是什么?如果您能分享您在项目中的使用方式,那就太好了!
使用弱哈希图通常是一个坏主意。首先,它很容易出错,但更糟糕的是,它通常用于实现某种缓存。
这意味着以下内容:您的程序在一段时间内运行良好,性能良好,在压力下,我们分配越来越多的内存(更多的请求 = 更多的内存压力 = 可能更多的缓存条目),然后导致 GC。
现在,当您的系统处于高压力下时,您不仅会获得 GC,还会丢失整个缓存,就在您最需要它的时候。这个问题不好玩,所以你至少必须使用一个合理大小的硬引用 LRU 缓存来缓解这个问题 - 你仍然可以使用 weakrefs,但只能作为额外的帮助。
我见过不止一个项目被那个"错误"击中。
我见过的最"明确明智"的弱引用使用是 Guava 的Striped
,它确实锁定了条纹。 基本上,如果当前没有线程持有对锁定变量的引用,那么就没有理由保留该锁,是吗? 该锁过去可能使用过,但现在没有必要了。
如果我必须给出一个规则来说明何时使用哪个,我会说当你将来可能使用某些东西时,软引用是可取的,但如果你真的需要它,可以重新计算它;当你可以确定在引用耗尽内存后永远不需要该值时,弱引用尤其可取。 (例如,如果对映射键类型的特定equals(Object)
实现使用默认引用相等性,并且对象停止在其他任何地方引用,则可以确保该条目永远不会再次被引用。
我使用弱引用的主要原因是间接地通过 WeakHashMap。
您可能希望在 Map 中存储对象集合(作为缓存或任何其他原因),但不希望它们在 Map 存在时一直保留在内存中,尤其是在对象相对较大的情况下。
通过使用 WeakHashMap,您可以确保来自 Map 的引用不是将对象保存在内存中的唯一内容,因为如果没有其他引用存在,它将被垃圾回收。
假设只要引用了一个对象就需要保留一些信息,但你不知道它什么时候会消失,你可以使用弱引用来跟踪信息。
是的,它有很好的影响。
上面"小部件序列号"问题的例子,最简单的办法就是使用内置的
WeakHashMap
类。WeakHashMap
的工作方式与HashMap
完全相同,除了键(不是值!)是使用弱引用引用的。如果WeakHashMap
密钥成为垃圾,则会自动删除其条目。这避免了我描述的陷阱,除了从HashMap
切换到WeakHashMap
之外不需要任何更改。如果您遵循通过Map
接口引用映射的标准约定,则其他代码甚至不需要了解更改。
参考
JVM平台需要弱引用对象,以确保防止内存泄漏。
正如Java开发人员应该知道的那样,Java可能会泄漏,比预期的要多。在不再使用 Object 但某些集合仍然强烈引用该实例的情况下,此声明尤其正确:一个非常简单但反复出现的内存泄漏示例,实际上在强引用存在之前不会释放内存区域。
在上述情况下,集合中的弱引用对象可确保:没有强引用链,但只有弱链,该实例可以作为 garbace 可收集。
在我看来,Java平台提供的所有功能在某种程度上都是有用的:非常熟练的程序员可以驱动Java快速可靠,C++编写非常高质量的代码。
与发布-订阅或事件总线解耦
当您想要让一个对象前往垃圾回收而不必优雅地将自身从持有引用的其他对象中删除时,WeakReference
很好。
在发布-订阅或事件总线等方案中,将保存对订阅对象的引用集合。这些引用应该是弱的,以便订阅者可以方便地超出范围,而无需费心取消订阅。订阅者可以在应用程序中的所有其他地方发布其强参考后"消失"。此时,订阅列表或事件总线无需继续挂在订阅者上。使用WeakReference
允许对象继续被遗忘。
可能在它不知情的情况下被订阅,由其他第三方对象提交到 pub-sub 或事件总线。在订阅者的生命周期后期协调取消订阅的调用可能非常麻烦。因此,让订阅者在没有正式取消订阅的情况下淡出可能会大大简化您的代码,并且可以避免在取消订阅协调失败时出现困难的错误。
下面是一组线程安全的弱引用的示例,如本答案和本答案所示。
this.subscribersSet =
Collections.synchronizedSet(
Collections.newSetFromMap(
new WeakHashMap <>()
)
);
请注意,在垃圾收集实际执行之前,集合中的条目实际上不会被删除,如上面的链接答案中所述。虽然作为垃圾收集的候选者存在(在任何地方都没有剩余的强引用),但该项目仍保留在集合中。
需要使 Java 垃圾回收具有确定性。(从略带讽刺的角度来看,有一些道理)。