финализатор не вызывается после GC.Collect () [дубликат]

Aug 17 2020

Я думаю, что упускаю что-то фундаментальное, и надеюсь, что вы можете помочь. Код ниже создает объект, удаляет ссылку и вызывает сборщик мусора. Я ожидал, что финализатор SomeClass будет вызываться при нахождении в Readline. Это не так. Я пробовал вызывать GC.Collect в цикле, добавляя несколько вызовов Sleep () для запуска потока финализатора. Не бывает.

Только когда Main заканчивается, финализатор попадает, но, что удивительно, ударяется дважды. Что мне не хватает?

class Program
{
    public static void Main(string[] args)
    {
        SomeClass some = new SomeClass("Hello World!");
        some = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Done");
        Console.ReadLine();
    }
}

class SomeClass 
{
    string ss;
    public SomeClass(string s) { ss = s; }
    ~SomeClass()
    {
        var hash = this.GetHashCode();
    }
}

Дополнение Существует разница в запуске программы в режиме отладки и в режиме выпуска. Приведенная ниже программа работает в режиме отладки, Start - Done - Finalizeтогда как в режиме выпуска отображается файл журнала Start - Finalize - Done. Последнее - то, что я ожидал.

class Program
{
    private static string logfile = @"c:\temp\log.txt";
    public static void Main(string[] args)
    {
        File.WriteAllText(logfile, "Start\n");
        SomeClass some = new SomeClass("Hello World!");
        some = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        File.AppendAllText(logfile, "Done\n");
    }
}

class SomeClass 
{
    private static string logfile = @"c:\temp\log.txt";
    public string SomeString { get; set; }
    public SomeClass(string s) { SomeString = s; }
    ~SomeClass()
    {
        File.AppendAllText(logfile, "Finalize\n");
    }
}

Ответы

1 V0ldek Aug 16 2020 at 23:33

Объекты, которые собираются сборщиком мусора, становятся пригодными для финализации и помещаются в очередь финализации. Нет абсолютно никакой гарантии, что эти финализаторы когда-либо будут запущены .

Я собираюсь перенаправить вас на замечательные посты Эрика Липперта по этому поводу с подходящим названием «Когда все, что вы знаете, неправильно». В частности:

Миф: Установка переменной в значение null приводит к запуску финализатора на объекте, на который ранее ссылалась переменная. Установка переменной в значение null не приводит к немедленному выполнению каких-либо действий, кроме изменения значения переменной. Если переменная была последней живой ссылкой на рассматриваемый объект, то этот факт будет обнаружен, когда сборщик мусора запустит сборщик для любого поколения, в котором находился объект. (Если он запускается вообще, что может и не быть. Нет никаких гарантий что GC работает.)

И даже с GC.Collect():

Миф: вызов GC.Collect()вызывает запуск финализаторов. Нет, это вызывает сбор. Это может идентифицировать объекты, которые являются кандидатами на финализацию, но не заставляет планировать поток финализатора. Если это то, что вы хотите - только для тестирования! - тогда звони GC.WaitForPendingFinalizers().

Так что лучший шанс на успех - у GC.WaitForPendingFinalizers(). Но даже тогда, ссылаясь на остальную часть сообщения ( и его продолжение ), ЗАПУСК ФИНАЛИЗАТОРА НЕ ГАРАНТИРУЕТСЯ . Не полагайтесь на это.

Я написал более подробное объяснение этого вопроса, чтобы вы могли использовать его в качестве отправной точки для дальнейших исследований.