В каких ситуациях будет указывать объект, который был поставлен в очередь для сборки мусора? - PullRequest
7 голосов
/ 29 февраля 2012

Я читаю тему C #, посвященную Dispose () и ~ finalize, и когда использовать какую.Автор утверждает, что вы не должны использовать ссылки в вашем ~ finalize, поскольку возможно, что объект, на который вы ссылаетесь, уже может быть собран.В частности, указано: ".. у вас есть два объекта, которые имеют ссылки друг на друга. Если объект № 1 собирается первым, то ссылка объекта № 2 на него указывает на объект, которого там больше нет."

В каких случаях экземпляр объекта будет находиться в состоянии, когда он имеет в памяти ссылку на объект, который находится в режиме GC?Я предполагаю, что есть как минимум два разных сценария: один, где ссылка на объект указывает на объект, и другой, где ссылка на объект указывает на ссылку на другой объект (например, когда он был передан с помощью ref в методе).

Ответы [ 6 ]

12 голосов
/ 29 февраля 2012

У вас могут быть объекты, которые ссылаются друг на друга, и весь набор может иметь право на GC.


Вот простой пример кода:

class Test { 
     public Test Other { get; set;} 

     static void Main()
     {
          Test one = new Test();
          Test two = new Test { Other = one; }
          one.Other = two;

          one = null;
          two = null
          // Both one and two still reference each other, but are now eligible for GC
     }
}
4 голосов
/ 29 февраля 2012

Обычно ГХ восстанавливает память только для объектов, на которые нет ссылок, указывающих на них. Однако объекты с финализаторами обрабатываются по-разному.

Вот что MSDN говорит об этом :

Для восстановления памяти, используемой объектами с методами Finalize, требуется как минимум две сборки мусора. Когда сборщик мусора выполняет коллекция, он восстанавливает память для недоступных объектов без финализаторы. В настоящее время он не может собирать недоступные объекты у которых есть финализаторы. Вместо этого он удаляет записи для этих объекты из очереди завершения и помещает их в список объекты помечены как готовые к доработке. [...]
Сборщик мусора вызывает методы Finalize для объектов в этом списке, а затем удаляет записи из списка. Будущая сборка мусора определит, что завершенные объекты действительно являются мусором, поскольку на них больше не указывают записи в списке объектов, помеченных как готовые к завершению.

Таким образом, нет никакой гарантии , что другие объекты, на которые есть ссылки в финализаторе, будут по-прежнему использоваться, когда GC выполнит метод Finalize , поскольку они, возможно, уже были Завершено во время более ранней сборки мусора, пока сам объект ожидал завершения .

2 голосов
/ 29 февраля 2012

Короче говоря, объекты, которые недоступны из корня GC (статическое поле, параметр метода, локальная переменная, зарегистрированная переменная) по цепочке ссылок, имеют право на сборку мусора. Таким образом, вполне возможно, что, скажем, объект A относится к B, который относится к C, который относится к D, но внезапно A обнуляет свою ссылку на B, и в этом случае все B, C и D могут быть собраны.

1 голос
/ 29 февраля 2012

К сожалению, существует много небрежного использования терминологии, связанной с сборкой мусора, что вызывает много путаницы.«Распорщик» или «финализатор» фактически не уничтожает объект, а скорее служит для задержки уничтожения объекта, который в противном случае будет иметь право на уничтожение до тех пор, пока он невозможность навести порядок в своих делах (то есть, как правило, сообщая другим людям, что их услуги больше не нужны).

Проще всего думать о сборщике мусора "останови мир", как о выполнении следующих шагов:по порядку:

  1. Уберите все элементы, которые являются достаточно новыми, чтобы их можно было считать "мусором".
  2. Посетите каждый корень сборки мусора (т. Е. Объект, который по своей природе является «живым»), и, если он еще не скопирован, скопируйте его в новую кучу, обновите ссылку, чтобы указать на новый объект, и и посетитевсе элементы, на которые он содержит ссылки (которые будут копировать их, если они не были скопированы).Если кто-то посещает элемент в старой куче, который был скопирован, просто обновите ссылку, которая использовалась для его просмотра.
  3. Изучите каждый предмет, который зарегистрирован для финализации.Если он еще не скопирован, отмените его регистрацию для завершения, но добавьте ссылку на него в список объектов, которые необходимо завершить как можно быстрее.
  4. Элементы в списке немедленного завершения считаются «живыми», но, поскольку они еще не были скопированы, посетите каждый элемент в этом списке и, если еще не скопировано, скопируйте его в новую кучу и посетите все элементына что он содержит ссылки.
  5. Оставьте старую кучу, так как никто больше не будет ссылаться на нее.

Интересно отметить, что в то время как некоторые другие системы сбора мусора работают с использованием двойных косвенных указателей для ссылок, сборщик мусора .net (по крайней мере, обычный "остановите мир") использует прямые указатели.Это несколько увеличивает объем работы, которую должен выполнять сборщик, но повышает эффективность кода, который манипулирует объектами.Поскольку большинство программ тратят больше времени на манипулирование объектами, чем на сбор мусора, это чистый выигрыш.

1 голос
/ 29 февраля 2012

"... тогда ссылка объекта №2 на него указывает на объект, которого там больше нет."

Это никогда не произойдет. Всякий раз, когда ваш код имеет доступ к ссылке, объект, на который ссылаются, все еще существует. Это называется безопасность памяти и остается в силе, когда объект завершается в фоновом режиме. Ссылка никогда указывает на собранный экземпляр.

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

class Foo
{
   private StreamWriter logFile = ...
   private StringBuilder sb = new StringBuilder("xx");    

   ~Foo()       
   {           
     if (sb.ToString() == "xx")  // this will always be safe and just work
     {
         // the next line might work or  
         // it might fail with "logFile already Disposed"          
         logFile.Writeline("Goodbye");  
      }             
   }
}
0 голосов
/ 29 февраля 2012

В .NET существует термин воскресение .

Короче говоря, воскрешение может произойти, когда ваш объект находится в очереди завершения, но когда вызывается финализатор (метод ~ClassName()), он перемещает объект обратно в игру.Например:

public class SomeClass
{
    public static SomeClass m_instance;

    ...

    ~SomeClass()
    {
    m_instance = this;
    }
}

Подробнее об этом можно прочитать здесь: Воскрешение объекта с помощью GC.ReRegisterForFinalize .Но я действительно рекомендую книгу Джеффри Рихтера CLR via C # , которая подробно объясняет этот вопрос в одной из глав.

...