С# GC.Collect() и память

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

Выполнит ли этот код мою цель? Прочитал много разных мнений и запутался.

public void Save(IList<Employee> employees)
    {
        // I've mapped the passed-in list
        var data = Mapper<Employee, EmployeeDTO>.MapList(employees);

        // ?????????????
        employees = null;
        GC.Collect();

        // Continues to process very long running methods....
        // I don't want this large list to stay in memory

   }

Может быть, мне следует использовать другую технику, о которой я не знаю?


person Big Daddy    schedule 09.09.2014    source источник
comment
Если список больше не используется, сборщик мусора автоматически соберет его, когда доступная память станет проблемой. Если у вас нет измеримой проблемы, не пытайтесь перехитрить GC.   -  person D Stanley    schedule 09.09.2014
comment
Если вам нужно быть очень осторожным с памятью в вашем приложении, не передавайте очень большие списки в качестве аргументов. Даже если GC выполняет свою работу, если у вас есть несколько потоков, обрабатывающих эти данные, очень большой список может выделяться несколько раз.   -  person xxbbcc    schedule 09.09.2014
comment
Ссылка передается по значению, поэтому по определению вызов по-прежнему укореняет список, что означает, что он не подходит для GC.   -  person Brian Rasmussen    schedule 09.09.2014
comment
@DStanley ... Это 64-битный .exe, работающий на сервере Windows 2008, поэтому я ограничен 8 ГБ оперативной памяти (так мне сказали). Я приближаюсь к этому и хочу избежать исключения OOM.   -  person Big Daddy    schedule 09.09.2014
comment
Лучший способ убедиться, что что-то удалено из памяти, — это просто убедиться, что все ссылки на него удалены, когда вы закончите его использовать. Период. Сборщик мусора будет работать сам по себе, когда это необходимо, и освободит все, что не имеет на него ссылки. Как уже было сказано, не пытайтесь перехитрить GC.   -  person Tony Vitabile    schedule 09.09.2014
comment
@xxbbcc, при передаче списка не будет выделено несколько строк. Вам нужно будет явно скопировать его, чтобы сделать это. Передается только ссылка на список (или если указать byref, то передается ссылка на ссылку).   -  person Samuel Neff    schedule 09.09.2014
comment
@BigDaddy GC освободит память, если сможет, чтобы избежать исключений OOM - если что-то содержит ссылку на него (и есть код, который будет/может использовать его), то GC не соберет ее даже если вы позвоните .Collect()   -  person D Stanley    schedule 09.09.2014
comment
Откуда изначально взялся большой список? Что именно вы с ним делаете? Если вы вносите изменения в группу сотрудников, можете ли вы сделать это с помощью оператора массового обновления вместо загрузки/сохранения?   -  person Samuel Neff    schedule 09.09.2014
comment
64-битная и поэтому ограниченная 8 ГБ - очень странная комбинация. Вам действительно нужно много работать, чтобы получить OOM в x64...   -  person Alexei Levenkov    schedule 09.09.2014
comment
удалить его из памяти или разрешить повторное использование? Вполне вероятно, что данные, которые были в списке, все еще будут в памяти после принудительного GC и останутся там до повторного использования/инициализации чем-то другим (даже если GC сможет собрать список, где вы вызываете Collect).   -  person Peter Ritchie    schedule 09.09.2014
comment
@SamuelNeff ... Это передано из другой сборки, над которой я не контролирую. Я сопоставляю его элементы с DTO, а затем сохраняю их в БД.   -  person Big Daddy    schedule 09.09.2014
comment
Иногда бывает выгоднее форсировать сбор детерминированно, в определенное время, чем если бы это происходило недетерминировано и рисковало бы случайным образом перестать отвечать на запросы приложения...   -  person Peter Ritchie    schedule 09.09.2014
comment
@PeterRitchie... Я не понимаю твой последний комментарий. Вы хотите сказать, что иногда можно использовать GC.Collect? Может быть, иногда мы, разработчики, знаем лучше, чем GC?   -  person Big Daddy    schedule 09.09.2014
comment
Судя по качеству респондентов и их ответам, я не понимаю, как кто-то может оправдать отрицание этого вопроса.   -  person Big Daddy    schedule 09.09.2014
comment
@SamuelNeff Я не говорил, что передача списка выделяет его несколько раз. Я сказал, что вызов этой функции в нескольких потоках (с якобы уникальными входными данными) выделит много памяти, если предполагается, что входной список очень велик. Если память вызывает беспокойство, то хранение большого списка в памяти — не лучший подход.   -  person xxbbcc    schedule 09.09.2014
comment
@xxbbcc, спасибо за разъяснение. Я не так интерпретировал ваш предыдущий комментарий.   -  person Samuel Neff    schedule 10.09.2014
comment
@SamuelNeff Да, я перечитал это и не уверен, что вы могли бы интерпретировать иначе. Я думал, что я был более ясным, когда я написал это.   -  person xxbbcc    schedule 10.09.2014


Ответы (2)


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

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

Если у вас нет измеримой проблемы, не пытайтесь перехитрить GC.

person D Stanley    schedule 09.09.2014
comment
Я близок к измеримой проблеме. Поскольку я запускаю 64-битное приложение на сервере Win 2008, я ограничен 8 ГБ памяти и быстро приближаюсь к этому порогу. Я никогда не рассматривал возможность манипулирования сборщиком мусора вручную, но у меня закончились идеи. - person Big Daddy; 09.09.2014
comment
В зависимости от того, какую версию Windows Server 2008 вы используете, вы, вероятно, физически не ограничены 8 ГБ памяти. У вас может быть установлено только 8 ГБ, но все версии Server 2008 и Server 2008 R2 (за исключением Server 2008 R2 Foundation) позволяют использовать гораздо больше 8 ГБ физической памяти при работе с архитектурой x64. См. эту страницу от Майкрософт. - person hunch_hunch; 09.09.2014
comment
@BigDaddy Тогда я бы посмотрел на обработку по частям (вверх по течению от Save) и / или убедился, что вы не держите список где-то еще. если вызывающий абонент просто передает список и больше ничего с ним не делает, то он должен быть собран при нехватке памяти. - person D Stanley; 09.09.2014

Это не прямой ответ на вопрос, а некоторые идеи о том, как справляться с ситуациями с нехваткой памяти, на которые ссылался постер (@Big Daddy).

Если вы сталкиваетесь с нехваткой памяти на платформе x64 с 8 ГБ памяти, вам следует определить, не является ли причиной этого ваше приложение. Если это так, то запустите профилировщик памяти (CLR Profiler или что-то еще или даже получите полный дамп пользователя и запустите на нем WinDbg), чтобы увидеть, что выделяет память. Возможно, у вас есть какие-то объекты, которые больше не используются, но все еще где-то упоминаются — это не настоящая утечка памяти, а память, которая не освобождается в вашем приложении — большинство приличных профилировщиков идентифицируют большие объекты (или объекты с большим количеством экземпляры) вместе с их типами.

Мне трудно поверить, что список, переданный этой функции Save, будет нагружать сервер с 8 ГБ памяти, но мы не знаем, сколько свободной памяти доступно для процесса и что это за процесс (IIS, рабочий стол , так далее.)

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

person xxbbcc    schedule 09.09.2014