Явно освобождаю память в c # - PullRequest
36 голосов
/ 05 мая 2009

Я создал приложение на c #, которое использует 150 МБ памяти (приватных байтов), в основном из-за большого словаря:

Dictionary<string, int> Txns = new Dictionary<string, int>();

Мне было интересно, как освободить эту память. Я пробовал это:

Txns = null;
GC.Collect();

Но, похоже, это не сильно влияет на мои личные байты - они уменьшаются с 155 МБ до 145 МБ. Любые подсказки?

Спасибо

-edit-

Хорошо, мне больше повезло с этим кодом (он уменьшает количество байтов до 50 Мб), но почему?

Txns.Clear(); // <- makes all the difference
Txns = null;
GC.Collect();

-edit-

Хорошо для всех, кто говорит «не используйте GC.collect», достаточно справедливо (я не собираюсь спорить об этом, кроме как сказать, что вы можете видеть, как проходит мой C-фон), но на самом деле он не отвечает мой вопрос: Почему сборщик мусора освобождает память, только если сначала очистить список транзакций? Разве это не должно освободить память, так как словарь был разыменован?

Ответы [ 9 ]

17 голосов
/ 05 мая 2009

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

11 голосов
/ 05 мая 2009

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

5 голосов
/ 05 мая 2009

Скорее всего, у вас есть скрытая ссылка на словарь в другом месте. Таким образом, словарь не собирается, но если вы Clear() его, содержимое будет собрано.

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

4 голосов
/ 05 мая 2009

Не уверен по памяти, если Dictionary имеет Dispose() или нет, но он обязательно должен иметь Clear(). Вызовите любой из них перед установкой любых ссылок на null.

Тогда просто позвольте сборщику мусора выполнить свою работу. Это почти никогда - хорошая идея назвать GC.Collect() явным образом самостоятельно, и это может даже не сделать то, что вы хотите / нуждаетесь / ожидаете, и в конечном итоге приведет к снижению производительности. Статический анализ кода (= FxCop) не предупреждает вас с правилом надежности CA2001 об этом ни за что, вы знаете? Просто не делайте этого, если вы действительно не знаете, что делаете. И даже тогда не делай этого. ; -)

Вы уверены, что словарь такой огромный? Разве это не просто 10 Мб памяти, а остальное занимает ваше приложение? Вопрос, который может вам помочь: Вы уже использовали профилировщик, чтобы увидеть, где фактически используется память ...?

1 голос
/ 20 сентября 2013

Хорошо, у меня есть теория ... Словарь - это коллекция KeyValuePair, которая снова является ссылочным типом.

Ваш словарь содержит эти keyValuePairs. Когда вы говорите:

Txns = null

Освобождает ссылку 'Txns' из коллекции KeyValuePair. Но все же на фактическую память 150 Мб ссылаются эти KeyValuePair, и они находятся в области видимости, поэтому не готовы к сборке мусора.

Но когда вы используете следующее:

Txns.Clear();
Txns = null;
GC.Collect();

Здесь метод clear также освобождает 150 МБ данных из соответствующих ссылок на объекты KeyValuePair. Таким образом, эти объекты были готовы для сбора мусора.

Это просто дикое предположение, которое я делаю здесь. Комментарии приветствуются:)

1 голос
/ 06 мая 2009

Вам нужна память назад? Память доступна, она просто не восстанавливается. Не следует очищать словарь, удерживать на нем слабую ссылку и позволить среде выполнения выполнять свою работу.

Если вы действительно хотите глубже понять, что происходит, посмотрите .NET Memory Profiler . Это даст вам представление о том, что именно происходит с вашим объектом, каково его поколение, какая память используется чем, и так далее. :)

1 голос
/ 05 мая 2009

Edit:

Если честно, установка ссылки на ноль не освобождает память, она назначает свой контейнер другому адресу, в данном случае ноль. Согласно MSDN , вызов Clear() делает это: «Свойство Count установлено в 0, и ссылки на другие объекты из элементов коллекции также освобождаются. Емкость остается неизменной».

...

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

Помимо размера словаря, вам не нужно беспокоиться о памяти, память - это не ваша проблема, это проблема сборщиков мусора.

Вызов Clear() удалит ссылки на любой содержащийся внутри объект, но емкость останется неизменной.

В техническом примечании сбор памяти дорог и требует значительных затрат времени. Причина в том, что GC не только обрабатывает кучу памяти и очищает вашу кучу, но и выполняет дефрагментацию кучи. Он пытается переместить память в непрерывные блоки, чтобы ускорить выделение, когда какой-то фрагмент кода выполняет большой запрос.

p.s. Насколько велик ваш словарь, что вы используете 155 МБ памяти?

0 голосов
/ 05 мая 2009

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

0 голосов
/ 05 мая 2009

Обычно не стоит пытаться форсировать GC. Вам действительно нужен целый словарь в памяти?

...