为什么垃圾收集器不对我的实例进行垃圾处理? [重复]

问题描述 投票:0回答:1

我正在编写一些测试以更好地了解.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流程中不做任何更改。

c# garbage-collection weak-references
1个回答
0
投票

这个问题的核心,您似乎想知道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内部运作方面的专家]

© www.soinside.com 2019 - 2024. All rights reserved.