Почему эта память не очищается или не выделяется вообще? - PullRequest
4 голосов
/ 23 июля 2010

Итак, у меня есть эта замечательная программа, которая очень полезна:

static void Main(string[] args)
{
    new Dictionary<int,int>(10000000);

    while (true)
    {
        System.Threading.Thread.Sleep(1000);
    }
}

Это даже не выдает никаких предупреждений от компилятора, что удивительно.

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

  1. Почему сборщик мусора никогда не очищает память, вместо этого позволяя системе перейти в состояние, когда памяти недостаточно для новых процессов?
  2. Черт, почему не оптимизировано распределение памяти? На него никогда не может ссылаться ничто!

Так что здесь происходит?

Ответы [ 4 ]

6 голосов
/ 23 июля 2010

Сборщик мусора недетерминирован и реагирует на нехватку памяти.Если ничто не требует памяти, она может не собираться некоторое время.Он не может оптимизировать new, так как это меняет ваш код: конструктор может иметь побочные эффекты.Кроме того, при отладке, скорее всего, будет решено не собирать.

В релизной / оптимизированной сборке я бы ожидал, что это соберет в некоторый момент, когда есть веская причина,Существует также GC.Collect, но этого, как правило, следует избегать, за исключением крайних сценариев или определенных требований профилирования.

Как «почему» - в поведении GC есть разница между «поколениями» GC;и у вас есть несколько больших массивов в «куче больших объектов» (LOH).Проверять этот LOH довольно дорого, что может объяснить, почему он так неохотен.

3 голосов
/ 23 июля 2010

Я предполагаю, что скрытая коллекция Gen0 выполняется.

Вот моя тестовая программа:

static void Main(string[] args)
{
    new Dictionary<int, int>(10000000);
    Thread.Sleep(5000);

    int x = 1; // or 0;
    int i = 0;
    while (true)
    {
        object o = ++i;
        Thread.Sleep(x);
    }
}

Когда система выполняет Sleep (1), система должна подумать, что это подходящее время для быстрого скрытого GC только на Gen0. Таким образом, оператор 'object o = ++ i' никогда не оказывает давления на Gen0 и никогда не запускает коллекцию GC и, следовательно, никогда не выпускает Словарь.

Сон (1) http://www.freeimagehosting.net/uploads/6fad1952e0.png

Измените x на 0. Теперь этот скрытый GC не возникает, и все работает как положено, с оператором 'object o = ++ i', вызывающим сбор словаря.

Сон (0) http://www.freeimagehosting.net/uploads/f285b8acdb.png

0 голосов
/ 23 июля 2010

GC, вероятно, запускается и освобождает память ... для самого приложения. То есть, если Sleep() вызовам нужно выделить некоторое ОЗУ, то он, вероятно, найдет его много, а именно большие блоки, которые изначально были выделены для огромного Dictionary.

Это не означает, что ГХ вернул память операционной системе. С точки зрения ОС, большие блоки все еще могут быть частью процесса, которые не могут использоваться другими процессами.

Распределение не оптимизировано, потому что это некоторый внешний код. Ваш класс Main вызывает конструктор для Dictionary<int,int>, который может сделать что угодно , возможно, с различными побочными эффектами. Как человек-программист, вы ожидаете , что у конструктора не будет внешних видимых побочных эффектов, но компилятор и виртуальная машина этого точно не знают. Таким образом, код не может обойтись без реального создания экземпляра Dictionary<int,int> и вызова его конструктора. Точно так же конструктор Dictionary<int,int> не знает, что он вызывается для объекта, который скоро станет недоступным, поэтому он не может оптимизировать себя.

0 голосов
/ 23 июля 2010

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

static void Main(string[] args)
{
    {
        new Dictionary(10000000);
    }

    while (true)
    {
        System.Threading.Thread.Sleep(1000);
    }
}

это должно теперь оставить память доступной для сборки мусора

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