19

基本区别在于,弱引用应该在每次运行 GC 时声明(保持低内存占用),而软引用应该保留在内存中,直到 GC 实际需要内存(它们试图延长生命周期但可能随时失败,这对于例如缓存非常有用,尤其是相当昂贵的对象)。

据我所知,对于弱引用如何影响 .NET 中对象的生命周期,没有明确的说明。如果它们是真正的弱引用,那么它们根本不应该影响它,但这也会使它们对于它们的缓存主要目的毫无用处(我认为我错了吗?)。另一方面,如果他们表现得像软裁判,他们的名字就会有点误导。

就个人而言,我认为它们的行为类似于软引用,但这只是一种印象,没有根据。

当然,实施细节适用。我在问与.NET 的弱引用相关的心态——它们是否能够延长生命周期,或者它们的行为是否像真正的弱引用?

(尽管有许多相关问题,但我还没有找到这个特定问题的答案。)

4

3 回答 3

14

C# 弱引用实际上是软的吗?

不。

我错了吗?

你错了。弱引用的目的绝对不是您所说的缓存。这是一个普遍的误解。

他们能够延长生命周期,还是表现得像真正的弱裁判?

不,它们不会延长使用寿命。

考虑以下程序(F# 代码):

do
  let x = System.WeakReference(Array.create 0 0)
  for i=1 to 10000000 do
    ignore(Array.create 0 0)
  if x.IsAlive then "alive" else "dead"
  |> printfn "Weak reference is %s"

此堆分配一个空数组,该数组可立即进行垃圾回收。然后我们循环 10M 次分配更多无法访问的数组。请注意,这根本不会增加内存压力,因此没有动机收集弱引用引用的数组。然而程序会打印“Weak reference is dead”,因为它仍然被收集了。这是弱引用的行为。软引用会一直保留到实际需要它的内存为止。

这是另一个测试程序(F# 代码):

open System

let isAlive (x: WeakReference) = x.IsAlive

do
  let mutable xs = []
  while true do
    xs <- WeakReference(Array.create 0 0)::List.filter isAlive xs
    printfn "%d" xs.Length

这会不断过滤掉无效的弱引用,并在链表的前面添加一个新的引用,每次都打印出链表的长度。在我的机器上,这永远不会超过 1,000 个幸存的弱引用。它在循环中上升然后下降到零,大概是因为所有弱引用都在每个 gen0 集合中收集。同样,这是弱引用而不是软引用的行为。

请注意,这种行为(在 gen0 集合中积极收集弱引用对象)正是使弱引用成为缓存的错误选择的原因。如果您尝试在缓存中使用弱引用,那么您会发现缓存被无缘无故地刷新了很多。

于 2013-01-06T20:41:01.937 回答
9

我没有看到任何信息表明它们会增加它们指向的对象的生命周期。我读到的关于 GC 用于确定可达性的算法的文章也没有以这种方式提及它们。所以我希望它们对对象的生命周期没有影响。

Weak
此句柄类型用于跟踪对象,但允许将其收集。收集对象时,GCHandle 的内容将归零。弱引用在终结器运行之前归零,所以即使终结器复活了对象,弱引用仍然归零。

WeakTrackResurrection
这种句柄类型类似于Weak,但如果对象在终结期间复活,则句柄不会归零。

http://msdn.microsoft.com/en-us/library/83y4ak54.aspx


有一些机制可以使无法访问的对象在垃圾收集中幸存下来。

  • 对象的世代大于发生的GC的世代。这对于在大对象堆上分配的大对象特别有趣,并且为此目的始终被视为 Gen2。
  • 带有终结器的对象和所有可从它们到达的对象都在 GC 中存活。
  • 可能存在一种机制,旧对象的先前引用可以使年轻对象保持活力,但我不确定。
于 2011-10-13T15:03:27.213 回答
-1


的,弱引用不会延长对象的生命周期,因此一旦所有强引用都超出范围,就可以对其进行垃圾回收。它们对于保持初始化成本很高的大型对象很有用,但如果它们没有被积极使用,它们应该可用于垃圾收集。

于 2011-10-13T14:49:35.490 回答