Why GC does not collect an unused object

2019-08-29 07:47发布

问题:

I'm trying to understand how the GC acts when an object is not being used anymore, my test is to do nothing with the object (after used) but it didn't work, the object's destructor was never called.

I've created an example program trying to wait until the object is destroyed, but after 4 hours running nothing happens.

Note: I know if I set the object to null the GC will collect it, but I just want to see the "normal" way the GC itself collects the object.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    internal class Program
    {
        private const string FilePath = @"C:\objLifeCycle.txt";

        private static void Main(string[] args)
        {
            var result = GetList();
            Console.WriteLine("Results received! Object Id: [{0}] time: [{1}]", result.ObjId, DateTime.Now);

            //result = null;

            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    Thread.Sleep(2000);
                    Console.WriteLine("...");
                    GC.Collect();
                }
            });

            Console.ReadKey();
        }

        private static LinkList GetList()
        {
            return new LinkList(FilePath) { "link1", "link2", "link3" };
        }
    }
}

internal class LinkList : List<string>
{
    internal string ObjId { get; set; }

    internal string FilePath { get; set; }

    internal LinkList(string filePath)
    {
        ObjId = Guid.NewGuid().ToString();
        FilePath = filePath;

        WriteFile($"Object LinkList with Id [{ObjId}] has been created at [{DateTime.Now}]");
    }

    ~LinkList()
    {
        WriteFile($"Object LinkList with Id [{ObjId}] has been destroyed at [{DateTime.Now}]");

        Environment.Exit(0);
    }

    private void WriteFile(string line)
    {
        var sw = new StreamWriter(FilePath, true);
        sw.WriteLine(line);
        sw.Close();
    }
}

回答1:

GC will not collect free resources in a deterministic Way. The program should need memory to ask for resources. If there are enough resources the GC won't collect anything because there is not necessary. Also the GC could collect memory only in program "Idle" states...

I strongly recommend you to read: Understanding .NET Garbage Collection

As you can test and see in documentation there is no way to force a garbage collection even calling GC.Collect()

And besides of that actually you need to understand that there are some GC flavors depending on platform and CLR implementations

Cross-VM Object Collections

For test purposes you can increase the need of Collect memory using GC.AddMemoryPressure Method (Int64)



回答2:

The GC will collect the object, but only in Release mode.

In Debug mode, the variable result will exist as long as it's in scope, even if you don't use it, so that the debugger can keep showing you its value. But this also means the object can't be garbage collected.

In Release mode, .Net is smart enough to realize the object will not be used anymore, so it will be eligible for garbage collection. And when the GC is invoked (in your case explicitly by calling GC.Collect()), it will collect it.