2016-05-18 5 views
0

MSDN говорит для GC.Collect()Weird поведения .NET сборщик мусора

Всех объектов, независимо от того, как долго они были в памяти, является считается для сбора; Однако объекты, на которые ссылаются в код не собран. Используйте этот метод, чтобы заставить систему попытаться восстановить максимальный объем доступной памяти.

Таким образом, я ожидал, что класс Child, который все еще упоминается в классе родителя, не будет собран до того, как будет получен Parent.

Но странно, что он МОЩНО собран до того, как собирается родитель. Это не имеет никакого смысла для меня.

Скомпилируйте следующий код на VS2010 и запустите его на фреймворке 4.0. Что я получаю это:

Garbage Collector Test Code

using System; 

namespace GarbageCollector 
{ 
    class Child 
    { 
     public bool bInUse = true; 
     public void Dispose() 
     { 
      Console.WriteLine("Child finished by Parent."); 
      bInUse = false; 
     } 

     ~Child() 
     { 
      bInUse = false; 
     } 
    } 

    class Parent 
    { 
     Child child = new Child(); 
     ~Parent() 
     { 
      if (!child.bInUse) 
       Console.WriteLine("Finalizing Child that is still in use in a Parent!"); 

      child.Dispose(); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      while (true) 
      { 
       for (int i=0; i<10; i++) 
       { 
        Parent P = new Parent(); 
       } 
       GC.Collect(); 
      } 
     } 
    } 
} 

Может кто-нибудь объяснить мне, что здесь происходит?


EDIT:

Я уже нашел, как решить эту проблему. Если вы хотите получить доступ к членам класса в Finalizer вашего класса, это может быть проблемой, если у этих членов также есть Finalizer. В этом случае участники могут уже быть мертвыми до того, как Finalizer вашего класса сможет получить к ним доступ, потому что GarbageCollector уничтожает их в ЛЮБОМ порядке. (Ребенок до рождения ребенка или ребенка после рождения)

НО, если вы получаете доступ к членам класса, у которых нет собственного финализатора, эта проблема не появляется.

Итак, если вы хотите сохранить, например, список ручек в своем классе, и вы хотите закрыть эти дескрипторы в Finalizer, убедитесь, что этот класс списка НЕ ​​имеет собственный Finalizer, иначе ваши дескрипторы могут исчезнуть раньше вы можете закрыть их!

+0

сборщик мусора довольно умный - вероятно, его обнаружение, что «список» не используется, поэтому он очищает его. –

+0

Я удалил ArrayList. Это не имеет никакого отношения к этому. – Elmue

+2

Включили ли вы оптимизацию? – D3C34C34D

ответ

8

Таким образом, я ожидал, что класс Child, который все еще упоминается в классе родителя, не будет собран до того, как будет получен Parent.

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

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

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

Итак, в конце концов, вся концепция, стоящая за цитируемым ожиданием, на самом деле не является разумной предпосылкой на разных уровнях.

+0

Спасибо за ваш ответ. Таким образом, это означает, что я не могу написать код в Finalizer, который обращается к любому из членов классов, потому что в тот момент они могут уже быть мертвыми - некоторые или все из них. Я не знал этого. – Elmue

+0

@Elmue Это буквально противоположность тому, что я сказал. Тем не менее, вы по-прежнему не должны получать доступ к управляемым свойствам объекта по многим другим причинам, но не потому, что его можно было собрать, потому что он фактически не может быть полностью собран до завершения финализатора. – Servy

+0

Как все наоборот? Вы написали: «... GC может удалить их в любом порядке ...», что означает, что Ребенок может быть мертв перед Родительским. Таким образом, ребенок уже может быть недоступен в Parent.Finalize(). Пожалуйста, прочтите мой EDIT. – Elmue

Смежные вопросы