Есть ли польза от обнуления ссылок для «поощрения» сборки мусора .NET? - PullRequest
8 голосов
/ 23 августа 2010

Предположим, есть следующий код:

foreach (...)
{
    List<int> localList = new List<int>(100);

    // do stuff

    localList = null;
}

Время от времени я испытываю искушение пустыми ссылками в тот момент, когда процедура собирается завершиться (т. Е. Возвращаться), или в этом случае, так же, как и в цикле. Есть ли польза от этого, даже если маленькая?

Ответы [ 8 ]

12 голосов
/ 23 августа 2010

Нет смысла делать это для локальных переменных. С помощью JIT CLR точно знает, когда локальная переменная больше не используется в методе и, следовательно, может быть собрана.

Рэймонд Чен недавно сделал очень подробную статью в блоге о том, когда именно предметы коллекционирования. Он подробно описывает этот сценарий и стоит прочитать

Однако есть несколько исключений из этого правила. Если рассматриваемая локальная переменная записывается в замыкание или итератор, то да, обнуление переменной дает эффект. А именно потому, что local больше не является local, а вместо этого является полем и имеет другую семантику GC.

4 голосов
/ 23 августа 2010

Нет.Фактически, «обнуление» локальных переменных может при некоторых обстоятельствах предотвратить сборку мусора.В обычной работе, как только переменная перестает быть доступной из какого-либо исполняемого кода, она становится доступной для сборки мусора.Если в конце вашего метода вы «обнулите» переменную, вы сохраните ее «живой» до этого момента и фактически задержите ее доступность для сборки мусора.

2 голосов
/ 23 августа 2010

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

Если ожидается, что время жизни (полезное или нет) объекта переживет полезное время жизни объекта, на который он ссылается, то ненужное удержание ссылки предотвратит последний объектот сбора до первого (т. е. ссылка заставит сохранить последний объект даже после того, как он стал бесполезным).Очистка ссылки поможет избежать этой проблемы.

Если у класса, написанного на vb.net, есть какие-либо «переменные WithEvents», они должны быть очищены (не заданы) ни в коем случае, когда объект, содержащий их, становится бесполезным.Класс нельзя собирать, пока он содержит ссылку на живой объект в «переменной WithEvents».Если, например, перечислитель содержит ссылку «WithEvents» на базовую коллекцию (например, чтобы он мог получать события, если / когда коллекция изменена) и его обработчик Dispose не очищает свою ссылку на базовую коллекцию, перечислитель будет поддерживаться какПока основная коллекция.Если коллекция перечисляется очень много раз, это может привести к серьезной утечке памяти.

2 голосов
/ 23 августа 2010

Я написал длинную и исчерпывающую статью в блоге, отвечающую на этот вопрос .

Подводя итог, можно сказать, что общий ответ "нет, выгоды нет". Однако есть несколько особых случаев:

  • Статические поля должны быть установлены в ноль, когда они больше не нужны - если только процесс не завершается, в этом случае установка статических полей в ноль не требуется.
  • Локальные переменные вряд ли когда-либо должны быть установлены в нуль. Есть только одно исключение: может быть полезно установить локальные переменные в нуль, если они выполняются в CLR не от Microsoft.
  • Поля экземпляра вряд ли когда-либо должны быть установлены в нуль. Существует только одно исключение: поле экземпляра может быть установлено в нуль, если ожидается, что ссылочный объект переживет ссылочный объект. [Обратите внимание, что обычная практика установки пустых полей экземпляра в IDisposable.Dispose не соответствует этому тесту и не должна поощряться].

В заключение: вообще говоря, устанавливать переменные в нуль, чтобы помочь сборщику мусора, не рекомендуется. Если это считается необходимым, то существует необычное условие, и оно должно быть тщательно задокументировано в коде.

2 голосов
/ 23 августа 2010

Нет, если объявление находится внутри блоков, как у вас. Как только он выйдет из области видимости, ссылка будет автоматически аннулирована и GC'd.

1 голос
/ 23 августа 2010

Нет.Позвольте GC выполнять свою работу и помогать ей только в случае необходимости.

0 голосов
/ 21 октября 2011

И ответ: да, если вы знаете, что делаете! Но это преждевременная оптимизация (корень всех зол для многих людей), поэтому вам не следует делать это, если вам действительно не нужна память.

Теперь я проанализирую только два случая, когда это может быть полезно.

  • Свойства объекта, на который вы знаете, больше не будут ссылаться (например, вы кэшировали некоторые сложные вычисления в частном свойстве. Вы знаете, что вам больше не нужно, вы чистите его) Но об этом было сказано многими людьми

  • Поля в сложных методах. Это противоположно тому, что сказали другие, но у меня есть пример, поэтому я знаю, что я прав :-) Теперь, обычно, в последовательном методе , GC может видеть, где локальная ссылка больше не используется (технически важная часть - это когда он больше не читается. И вызов методов переменной - это «чтение». Запись в переменную не считается, потому что если никто не будет читать новое значение, писать бесполезно она)

Но я сказал последовательный метод . И непоследовательные методы? Давай попробуем! (ясно, что я уже сделал это! Я не буду делать пример ideone , потому что GC mono отличается от GC из .NET).

Скомпилируйте этот фрагмент кода в режиме Release и запустите без отладчика (т. Е. CTRL-F5) и просмотрите результаты:

using System;

namespace ConsoleApplication17
{
    class Program
    {
        static void Main()
        {
            const int cycles = 1000000;

            {
                string str = new string(' ', 10000000);

                Console.WriteLine("Let's see when the GC will free the memory");
                Console.WriteLine("Start: {0}", GC.GetTotalMemory(true));

                for (int i = 0; i < 1000000; i++)
                {
                    if (i == cycles - 1)
                    {
                        Console.WriteLine("Near end: {0}", GC.GetTotalMemory(true));
                    }

                    //Here we reference the string, 
                    //but only in the first 100 iterations
                    if (i < 100 && str[str.Length - 1] == 'c')
                    {
                        throw new Exception();
                    }
                }

                Console.WriteLine("End: {0}", GC.GetTotalMemory(true));
            }

            Console.WriteLine();

            {
                string str = new string(' ', 10000000);

                Console.WriteLine("Let's do the work for him");
                Console.WriteLine("Start: {0}", GC.GetTotalMemory(true));

                for (int i = 0; i < 1000000; i++)
                {
                    if (i == cycles - 1)
                    {
                        Console.WriteLine("Near end: {0}", GC.GetTotalMemory(true));
                    }

                    //Here we reference the string, 
                    //but only in the first 100 iterations
                    if (i < 100 && str[str.Length - 1] == 'c')
                    {
                        throw new Exception();
                    }
                    else if (i == 100)
                    {
                        str = null;
                        Console.WriteLine("Just nullified the string: {0}", GC.GetTotalMemory(true));
                    }
                }

                Console.WriteLine("End: {0}", GC.GetTotalMemory(true));
            }

            Console.ReadKey();
        }
    }
}

Результаты:

Let's see when the GC will free the memory
Start: 20042264
Near end: 20042888
End: 42872

Let's do the work for him
Start: 20042888
Just nullified the string: 42872
Near end: 42872
End: 42872

Объяснение: эта программа является примером циклической программы, в которой большой объект инициализируется вне цикла (цикл for), а большой объект используется только в части цикла (например, в первых 100 итераций). Ясно, что GC не может легко увидеть, что с конца итерации 99 (сотая итерация, 0-99) и далее на объект не будут ссылаться.

Но помните слова преждевременная оптимизация и корень всех зол ! : -)

0 голосов
/ 23 августа 2010

Есть один важный случай, когда обнуление имеет эффект, когда вы используете отладочную сборку с некоторыми отключенными оптимизациями jit.Много более забавного поведения в отношении того, когда объекты очищаются, может остановиться, чтобы упростить отладку.

Это важно, потому что это может привести к неверному представлению о том, что происходит в сборке выпуска.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...