GC не собирает, когда WeakReference ссылается на делегата?

Мне кажется, что когда я использую класс WeakReference в методе делегата класса объекта, класс объекта собирается GC, но есть еще одна его копия, находящаяся в WeakReference?

Мне трудно объяснить словами. Я приведу пример. У меня есть следующий класс объектов с именем TestObject:

class TestObject
{
    public string message = "";

    private delegate string deleg();

    public TestObject(string msg)
    {
        message = msg;
    }

    public Delegate GetMethod()
    {
        deleg tmp = this.TestMethod;
        return tmp;
    }

    public string TestMethod()
    {
        return message;
    }

}

Теперь в моем основном приложении я пытаюсь сослаться на метод TestMethod в TestObject через WeakReference. Цель состоит в том, чтобы сборщик мусора мог собрать TestObject, когда все жесткие ссылки исчезнут. Вот как выглядит мое основное приложение:

static void Main(string[] args)
    {
        var list = new List<WeakReference>();
        var obj = new TestObject("Hello 1");
        list.Add(new WeakReference(obj.GetMethod()));
        Console.WriteLine("Initial obj: " + ((Delegate)list[0].Target).DynamicInvoke());      //Works fine
        obj = null;     //Now, obj is set to null, the TestObject("Hello 1") can be collected by GC
        GC.Collect();   //Force GC
        Console.WriteLine("Is obj null: " + ((obj) == null ? "True" : "False"));
        Console.WriteLine("After GC collection: " + ((Delegate)list[0].Target).DynamicInvoke());
        Console.ReadKey();
    }

Это вывод, когда я запускаю приведенный выше код:

введите здесь описание изображения

Вот что странно. В первой строке obj мог напечатать "Hello 1", потому что он только что был инициализирован, а obj содержал ссылку на TestObject. Все верно. Затем obj было установлено на null с obj = null, и сборщик мусора был вынужден собирать. Таким образом, во второй строке вывода obj равно true нулю. Наконец, в последней строке, поскольку сборщик мусора собрал obj, я ожидаю, что он либо выдаст NullReferenceException, либо просто ничего не напечатает на выходе. Однако на самом деле он напечатал то же самое, что и в первой строке вывода! Разве TestObject уже не должен быть собран GC в этот момент?!

Возникает вопрос, были ли TestObject, которые сначала хранились в obj, были позже собраны GC или нет после того, как я установил для obj значение null.

Если бы я передал весь объект в WeakReference, т.е. new WeakReference(obj), а не делегат в WeakReference, все бы работало отлично.

К сожалению, в моем коде мне нужно передать WeakReference делегата. Как я могу заставить WeakReference работать правильно, чтобы GC мог собирать объект, только сославшись на делегата?


person Carven    schedule 22.12.2012    source источник


Ответы (2)


Думаю проблема в вашем тесте, а не во фреймворке. Похоже, что установка для локальной переменной значения null не делает того, что вы ожидаете. Если мы полностью пропустим локальную переменную, мы получим ожидаемое исключение NullReferenceException в строке «после»:

static void Main(string[] args)
{
    var list = new List<WeakReference>();
    //var obj = new TestObject("Hello 1");
    list.Add(new WeakReference(new TestObject("Hello 1").GetMethod()));
    Console.WriteLine("Initial obj: " + ((Delegate)list[0].Target).DynamicInvoke());      //Works fine
    //obj = null;     //Now, obj is set to null, the TestObject("Hello 1") can be collected by GC
    GC.Collect();   //Force GC
    //Console.WriteLine("Is obj null: " + ((obj) == null ? "True" : "False"));
    Console.WriteLine("After GC collection: " + ((Delegate)list[0].Target).DynamicInvoke());
    Console.ReadKey();
}
person Av Pinzur    schedule 23.12.2012

На самом деле ваш пример будет работать, как и ожидалось, поскольку у вас нет ничего, кроме WeakReference, ссылающегося на делегата, поэтому GC может его собрать, даже если вы закомментируете строку «obj = null»!

Это мой результат

Может быть, потому что я запускаю код в отдельном потоке (помимо основного потока)?

person horeaper    schedule 15.10.2013