5

问题是如何测试对象在调用 finalize 时释放资源的事实。类的代码:

public class TestClass : IDisposable {

    public bool HasBeenDisposed {get; private set; }

    public void Dispose() {
        HasBeenDisposed = true;
    }

    ~TestClass() {
        Dispose();
    }
}

请注意,我现在并不关心 Dispose/Finalize 的正确实现,因为我想先找到测试它的方法。在这个阶段,假设如果调用 Dispose/Finalize 件, HasBeenDisposed将设置为 true 就足够了。

我写的实际测试看起来像:
UPDATED WITH WEAKREFERENCE

[Test]
public void IsCleanedUpOnGarbadgeCollection() {
    var o = new TestClass();
    o.HasBeenDisposed.Should().Be.False();

    **var weak = new WeakReference(o, true); // true =Track after finalisation
    o = null; // Make eligible for GC**

    GC.Collect(0, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();


    **((TestClass)weak.Target)**.HasBeenDisposed.Should().Be.True();
}

或我更喜欢的代码(更新后添加):

[Test]
public void IsCleanedUpOnGarbadgeCollection() {
    WeakReference weak = null;

    // Use action to isolate instance and make them eligible for GC
    // Use WeakReference to track the object after finalisaiton
    Action act = () = {
        var o = new TestClass();
        o.HasBeenDisposed.Should().Be.False();
        weak = new WeakReference(o, true); // True=Track reference AFTER Finalize
    };

    act();

    GC.Collect(0, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();

    // No access to o variable here which forces us to use WeakReference only to avoid error
    ((TestClass)weak.Target).HasBeenDisposed.Should().Be.True();
}

该测试失败(PASSES AFTER UPDATE)但我观察到以下(更新):

  1. GC.WaitForPendingFinalizers() 确实挂起线程并最终确定o中的实例,但前提是没有植根。为其分配 NULL 并在最终确定后使用 Wea​​kReference 获取它。
  2. 当o不保存实例时,在正确的点执行完成(析构函数)代码。

那么测试这个的正确方法是什么。我想念什么?

我想是变量o阻止了 GC 收集它。
更新:是的,这是问题所在。不得不改用弱引用。

4

3 回答 3

3

“我想是变量 o 阻止了 GC 收集它。” 正确的。堆栈上存在引用意味着对象是可访问的,因此不符合收集(和终结)的条件。

因为一个对象在没有对它的引用之前不会被终结,所以测试终结行为可能会很棘手。(您需要对对象的引用才能对其进行断言!)一种方法是间接进行:让对象在最终确定期间发送某种消息。但这纯粹出于测试目的而扭曲了最终代码。您还可以持有对该对象的弱引用,这将使其有资格进行终结,并让它在终结器中自我复活——但同样您不希望它在生产代码中自我复活。

于 2009-10-21T01:13:04.747 回答
0

如果有对它的本地引用,为什么要收集该对象?

于 2009-10-21T01:12:21.680 回答
0

A memory profiler is the most appropriate way to test for leaks.

I could recommend the .Net Memory Profiler.

于 2010-12-14T20:18:51.173 回答