为什么垃圾收集器不会对我的实例进行垃圾处理?

我正在编写一些测试,以更好地理解.NET垃圾收集器的工作方式,以便构建没有内存泄漏的框架。但是在我的第一个非常简单的测试中,我面临着意外的行为。

这是我对GC的了解:

  • 它会定期(并在可能的时候)清理事物
  • 它清除不再引用的实例

这是我为验证我的知识而编写的小班课程:

public class People
{
    private People _child;
    private WeakReference<People> _parent = new WeakReference<People>(null);

    public void AddChild(People child)
    {
        _child = child;
        _child._parent.SetTarget(this);
    }
}

基本上,父母可以引用其子女。根据我的上述知识,我希望当父母“死亡”时,其子女也是如此。

这里的小技巧是使用WeakReference,以便孩子可以访问其父对象,但无需创建可能导致内存泄漏的循环引用(这是我试图找出的重点之一) :是否有两个实例仅互相引用垃圾收集?或者换句话说:在这种情况下我是否必须使用WeakReference?我猜是如果它们直接互相引用也不会被垃圾收集,但是我实际上从未检查过它。)

这是我用xUnit写的小测试:

public class GCTests
{
    [Fact]
    public void TestGC()
    {
        var parent = new People();

        var weakParent = new WeakReference(parent);
        var child = new WeakReference(new People());

        parent.AddChild(child.Target as People);

        // Until now,there is a reference to the parent so the GC is not supposed to collect anything

        parent = null;

        // But now,no-one is referencing the parent so I expect the GC to collect it and removed it from memory

        // Forces the GC to collect unreferenced instances
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        Assert.False(weakParent.IsAlive);
        Assert.False(child.IsAlive);
    }
}

Assert.False(weakParent.IsAlive)上测试失败,这意味着仍然有人引用了实际的父母。

我还尝试使用Thread.Sleep(10000);给GC时间来收集东西,但是在该断言上仍然失败。

所以我的问题是:为什么我的实例没有被垃圾收集

  • 我的哪一个断言是错误的?
  • 在垃圾回收过程中或在使用WeakReference时我会误解什么?

有关信息,我正在使用目标.NET Core 3的xUnit测试项目,但我希望它不会改变GC流程。

sunshine7993 回答:为什么垃圾收集器不会对我的实例进行垃圾处理?

这个问题的核心是,您似乎想知道GC是否可以收集唯一引用为循环的对象。我想我可以通过创建两个只能互相引用的非常大的对象,然后让它们超出范围并创建第三个大对象来进行测试,以查看是否可以引入一些内存压力来尝试使GC达到免费的东西。如果GC可以释放互相引用的对象,则程序消耗的内存应在某个时候减少。如果GC无法完成,那么内存使用量应该只会增加:

using System.Threading;

namespace ConsoleApp
{
    class Program
    {

        static void Main()
        {
            Thread.Sleep(2000);

            SetupBigThings();

            Thread.Sleep(2000);

            string big = new string('a',1000000000);


            while (true)
            {
                Thread.Sleep(2000);
            }
        }

        static void SetupBigThings()
        {
            Thread.Sleep(1000);
            BigThing x = new BigThing('x');
            Thread.Sleep(1000);
            BigThing y = new BigThing('y') { OtherBigThing = x };
            x.OtherBigThing = y;
            Thread.Sleep(1000);

        }

    }

    class BigThing
    {
        public BigThing OtherBigThing { get; set; }

        private string big;

        public BigThing(char c)
        {
            big = new string(c,750000000);
        }
    }
}

查看代码,我们应该看到内存高峰在3秒,然后在4秒再次。.5秒后,大对象超出范围,也许当下一个大对象在7秒左右被GC处理对象已创建

几乎就是该图所示:

enter image description here

因此,我认为GC确实可以收集仅相互引用的对象。仅仅说“哪个对象有0个引用?”可能不是天真,而是追逐引用路径,并且仅引用已经考虑用于GC的另一个节点的任何对象也被视为GC'able。我不是GC内部运作的专家
本文链接:https://www.f2er.com/3127983.html

大家都在问