为什么.NET没有像Java一样的SoftReference和WeakReference?

19

我非常喜欢WeakReference。但是我希望有一种方法可以告诉CLR你认为这个引用的弱化程度是多少(比如说从1到5的一个等级)。那会很棒。

Java有SoftReference、WeakReference和第三种类型叫做“phantom reference”。这就是3个层次,GC在决定是否回收对象时采用了不同的行为算法。

我考虑对.NET的WeakReference进行子类化(幸运的是,它不是sealed的,有这个可能),以创建一个基于到期计时器或其他方式的伪SoftReference。


2
也许如果您解释一下您要解决的问题,您可能会得到一些好的解决方案的答案。通常在这个粒度级别工作并不是一个好主意,某种形式的过期缓存更为合适,但这取决于问题。 - Pop Catalin
9个回答

19

我认为NET没有软引用的根本原因是它可以依赖虚拟内存的操作系统。Java进程必须指定其最大的操作系统内存(例如,使用-Xmx128M),而它永远不会占用超过该内存量的OS内存。而.NET进程会不断地获取需要的OS内存,当RAM用尽时,操作系统会提供具有磁盘支持的虚拟内存。如果.NET允许软引用,那么.NET运行时将无法知道何时释放它们,除非它深入OS中查看其内存是否实际上已经分页到磁盘(一个令人讨厌的OS/CLR依赖),或者请求运行时指定最大进程内存占用(例如与-Xmx相等的等效参数)。我想微软不想在.NET中添加-Xmx,因为他们认为操作系统应该决定每个进程得到多少RAM(通过选择哪些虚拟内存页面在RAM或磁盘上保留),而不是进程本身。


但是对于 .Net Compact 来说,这样的东西可能会很有用。 - i486

12
Java的SoftReferences被用于创建内存敏感的缓存(除此之外没有其他用途)。
在.NET 4中,.NET有一个类System.Runtime.Caching.MemoryCache,可能会满足任何这样的需求。

这是一个非常好的指针,应该放在答案的顶部。 - user430788
这是一个不错的补充,但我对它的灵活性有所疑虑。总的来说,我更喜欢暴露机制而非经销商黑匣子。它们在实际生产中往往更有用。(尽管GC本身通常是一个密封的黑匣子,但至少Java允许您替换整个收集器,如果您真的想这样做的话。) - user430788
我同意,一般来说 - 我更喜欢暴露的机制而不是黑匣子,这也是我非常喜欢Java而不喜欢C#的原因之一。但另一方面,每个新的Java垃圾回收器都需要自己实现SoftReferences,这可能比您当前的GC更不适合您的目的。而且,在低暂停/低延迟环境中更改GC并不是轻松的任务(至少如此)。 - barneypitt

5
拥有具有不同弱化程度(优先级)的WeakReference听起来很不错,但这也可能会使GC的工作更加困难而不是更容易。我不知道GC内部的情况,但我认为应该为WeakReference对象保留某种额外的访问统计信息,以便GC可以有效地清除它们(例如,可能会首先清除最少使用的项)。
很可能增加的复杂性并没有使任何事情更加高效,因为最有效的方式是首先清除不经常使用的WeakReferences。如果您可以分配优先级,您将如何进行分配?这听起来像是一种过早的优化:程序员大多数情况下并不真正了解并在猜测;结果是一个更慢的GC收集周期,可能会回收错误的对象。
这引出了一个问题,如果您关心WeakReference.Target对象被回收,那么使用WeakReference真的是一个好的选择吗?
就好像一个缓存。您将东西塞进缓存中,并要求缓存在x分钟后使其过期,但大多数缓存都不能保证完全保留它。它只保证如果它确实保留了,它将根据请求的策略过期。

区别在于使用SoftReference缓存允许您在有足够内存时保留物品,但如果没有足够内存,则将其处理掉。这比对缓存设置超时时间是一个高效的解决方案。 - user430788
@user430788,我认为你忽略了我的有关缓存的评论。缓存已经可以使用WeakReference了,但是缓存项也可能变得过时。从缓存消费者的角度来看,缓存通常不保证项目一直存在于缓存中。开发人员已经抱怨GC了,因此C#团队可能认为添加新的弱引用级别(软、幻象)并没有太大的意义。像往常一样,Eric Lippert的http://blogs.msdn.com/b/ericlippert/archive/2003/10/28/how-many-microsoft-employees-does-it-take-to-change-a-lightbulb.aspx 是了解C#特性的好读物。 - Robert Paulson
2
罗伯特,我担心你完全没有理解我的意思。不,缓存并不能保证事物始终留在缓存中。这就是关键所在。但仅基于时间的缓存不能很好地确定应该和不应该存在内存中的内容。它会无缘无故地清空一些内容。一个基于内存需求的缓存能更好地保留数据,除非确实需要删除。你可以自己实现一个LRU缓存,但那将需要向你的应用程序添加伪内存管理代码。让已经管理内存的GC管理你的缓存要更加有效率。 - user430788

4

我猜尚未出现这种情况的原因是为了简单。我认为,大多数人会认为只有一种引用类型是一种美德,而不是四种。


7
实际上,软引用非常有用,因为它们允许基于垃圾回收的内存敏感缓存。实际理解软引用用途的大多数人都希望拥有它们。在Java代码中,我使用软引用比弱引用更频繁。 - user430788

3
也许 ASP.NET Cache 类(System.Web.Caching.Cache)可以帮助实现您想要的功能?如果内存不足,它会自动移除对象: 这里有一篇文章展示了如何在 Windows 窗体应用程序中使用 Cache 类。
引自:Equivalent to SoftReference in .net?

2
不要忘记您还有标准参考资料(日常使用的参考资料),这使您拥有更高一级。当您不关心对象是否消失时,应使用WeakReferences,而当您宁愿清除对象而不是内存不足时,应仅使用SoftReferences,就像使用普通引用一样。我不确定具体细节,但我怀疑在确定哪些对象是活动的时,GC通常会跟踪SoftReferences而不是WeakReferences,但在内存不足时也会跳过SoftReferences。我猜测.NET设计师们认为大多数人对差异感到困惑,或者SoftReferences增加了他们不想要的复杂性,因此决定将它们留出。顺便说一下,据我所知,PhantomReferences主要是为虚拟机内部使用而设计的,并不适用于实际客户端使用。

1
请看我上面有关SoftReferences有多有用以及它们如何允许您更有效地编写代码的评论。虚引用是为那些真正需要进行某种死后清理的人准备的。 - user430788

1
也许应该有一个属性,可以指定对象在被收集之前的代数。如果您指定为1,则是最弱的引用。但如果您指定为3,则需要在至少3次先前的收集中存活,才能考虑进行收集。
我认为跟踪复活标志对此无济于事,因为那时对象已经被终结了?不过可能我错了...
(附言:我是楼主,刚注册。很烦人,它不会从“未注册”帐户继承您的历史记录。)

0
不知道为什么.NET没有软引用。但是在Java中,软引用我认为被过度使用了。原因是至少在应用程序服务器中,您希望能够按应用程序影响Softreferenzen的存活时间的长短。目前在Java中无法实现这一点。

在HotSpot中,有一个选项可以稍微配置一下:-XX:SoftRefLRUPolicyMSPerMB=xyz。请参阅http://java.sun.com/docs/hotspot/HotSpotFAQ.html#gc_softrefs。 - Luke Quinane

0

也许在构造函数中寻找传递的“trackResurrection”选项?

GC类还提供了一些帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接