强制对 WeakReference 的目标进行垃圾回收

Force target of WeakReference to be garbage collected

在我的程序中,我有一个服务对象,它保持对对象的弱引用。我有一个自动测试断言,当删除对该对象的所有引用时,弱引用将被释放。测试的目的是证明服务不会导致对象在没有理由保留的情况下保留在内存中。

这是该测试的简化版本:

[Test]
public void WeakReference()
{
    var o = Allocate();
    var reference = new WeakReference(o.Value);
    Assert.IsTrue(reference.IsAlive);

    o.Value = null;
    GC.Collect();
    Assert.IsFalse(reference.IsAlive);
}

private MyObject Allocate()
{
    return new MyObject
    {
        Value = new object()
    };
}

private class MyObject
{
    public object Value { get; set; }
}

在 .NET 4.8 NUnit 测试项目中,此测试通过。但是,在项目的当前分支上,我在 .NET 5 项目中进行了测试。在那个项目中,同样的测试代码失败了,因为弱引用仍然“活着”。

我还尝试将 GC.Collect() 调用更改为

GC.Collect(int.MaxValue, GCCollectionMode.Forced, blocking: true);

没有骰子。

垃圾回收在 .NET 5 中的工作方式是否发生了根本性变化,导致上述代码不再有效?我怎样才能证明我的服务对象没有保留在内存中的对象上?

在这 3 个条件下,我能够使您的测试通过:

  1. 循环 GC.Collect()IsAlive.
  2. 编译器优化开启
  3. 调试器附加。

此修改后的测试代码演示了上面 #1 中提到的循环,并多次重复测试。它总是为我而过。

[Test, Repeat(1000)]
public void WeakReference()
{
    var o = Allocate();
    var reference = new WeakReference(o.Value);
    Assert.IsTrue(reference.IsAlive);

    o.Value = null;

    for (var i = 0; i < 2 && reference.IsAlive; i++)
        GC.Collect();

    Assert.IsFalse(reference.IsAlive);
}

我还没有足够的知识来说明为什么这会起作用,但我收集到它与导致对象引用成为 GC 根的条件有关GC).