Это плохая практика, чтобы зависеть от .NET автоматического сборщика мусора? - PullRequest
5 голосов
/ 23 сентября 2011

Можно создать множество объектов, интенсивно использующих память, а затем отказаться от ссылок на них.Например, я могу захотеть загрузить и обработать некоторые данные из базы данных, и я сделаю 100 отдельных итераций загрузки и обработки.Я мог бы объявить переменную DataTable один раз, и для каждого запроса сбрасывать ее в новый объект DataTable, используя конструктор, оставляя старый объект DataTable в памяти.он использует, в том числе Rows.Clear () и .Dispose ().Таким образом, я мог сделать это в конце каждой итерации перед установкой переменной в новый объект DataTable.ИЛИ я мог бы забыть об этом и просто позволить сборщику мусора CLR сделать это для меня.Сборщик мусора кажется довольно эффективным, поэтому конечный результат должен быть одинаковым в любом случае.«Лучше» явно распоряжаться объектами, интенсивно работающими с памятью, когда они вам не нужны (но для этого нужно добавить код) или просто зависеть от сборщика мусора, который сделает всю работу за вас (вы зависитеалгоритм GC, но ваш код меньше)?

По запросу приведен код, иллюстрирующий пример переработанной переменной DataTable:

    // queryList is list of 100 SELECT queries generated somewhere else.
    // Each of them returns a million rows with 10 columns.
    List<string> queryList = GetQueries(@"\\someserver\bunch-o-queries.txt");
    DataTable workingTable;

    using (OdbcConnection con = new OdbcConnection("a connection string")) {
        using (OdbcDataAdapter adpt = new OdbcDataAdapter("", con)) {
            foreach (string sql in queryList) {
                workingTable = new DataTable();  // A new table is created. Previous one is abandoned
                adpt.SelectCommand.CommandText = sql;
                adpt.Fill(workingTable);
                CalcRankingInfo(workingTable);
                PushResultsToAnotherDatabase(workingTable);
                // Here I could call workingTable.Dispose() or workingTable.Rows.Clear()
                // or I could do nothing and hope the garbage collector cleans up my
                // enormous DataTable automatically.
            }   
        }
    }

Ответы [ 3 ]

7 голосов
/ 07 декабря 2011

@ Джастин

... так что, чтобы ответить на ваш вопрос, нет. Нет ничего плохого в том, чтобы зависеть от .NET Garbage Collector.Напротив, на самом деле.

Это ужасная практика - полагаться на то, что ГК очистит вас.К сожалению, вы рекомендуете это.Это может привести к утечке памяти, и да, в .NET есть как минимум 22 способа «утечки памяти».Я работал с огромным количеством клиентов, которые диагностировали как утечки управляемой, так и неуправляемой памяти, предлагая решения для них, и рассказали нескольким группам пользователей .NET о Advanced GC Internals и о том, как работает управление памятью изнутри GC и CLR.

@ OP: Вы должны вызвать Dispose () для DataTable и явно установить его равным нулю в конце цикла.Это явно говорит GC, что вы закончили с этим, и больше нет корневых ссылок на него.DataTable помещается на LOH из-за его большого размера.Если вы этого не сделаете, вы можете легко фрагментировать ваш LOH, что приведет к исключению OutOfMemoryException.Помните, что LOH никогда не уплотняется!

Дополнительные сведения см. В моем ответе по адресу

Что произойдет, если я не вызову Dispose для объекта пера?

@ Henk - Там есть взаимосвязь между IDisposable и управлением памятью;IDisposable допускает полуявное освобождение ресурсов (если реализовано правильно).А с ресурсами всегда связана какая-то управляемая и , как правило, неуправляемая память.

Несколько замечаний по поводу Dispose () и IDisposable здесь:

  1. IDisposable обеспечивает удаление как Управляемой, так и Неуправляемой памяти.Утилизация неуправляемой памяти должна выполняться в методе Dispose, и вы должны предоставить Финализатор для вашей реализации IDisposable.

  2. GC не вызывает для вас Dispose.

  3. Если вы не вызываете Dispose (), GC отправляет его в очередь финализации и, в конечном счете, снова в очередь f-достижимости.Финализация заставляет объект выживать 2 коллекций, что означает, что он будет повышен до Gen1, если он был в Gen0, и до Gen2, если он был в Gen1.В вашем случае объект находится на LOH, поэтому он сохраняется до тех пор, пока не будет выполнен полный сборщик мусора (все поколения плюс LOH) дважды , что в «здоровом» приложении .NET представляет собой одну полную коллекциювыполняется ок.1 на каждые 100 коллекций.Поскольку в зависимости от вашей реализации на кучу LOH и GC оказывается большое давление, полные GC будут срабатывать чаще.Это нежелательно по соображениям производительности, поскольку полный сборщик мусора занимает гораздо больше времени.Кроме того, существует зависимость от того, какой тип GC вы используете, и используете ли вы LatencyModes (будьте очень осторожны с этим).Даже если вы используете Background GC (он заменил Concurrent GC в CLR 4.0), эфемерная коллекция (Gen0 и Gen1) по-прежнему блокирует / приостанавливает потоки.Это означает, что никакие распределения не могут быть выполнены в течение этого времени.Вы можете использовать PerfMon для мониторинга поведения использования памяти и активности GC в вашем приложении.Обратите внимание, что счетчики ГХ обновляются только после проведения ГХ.Для получения дополнительной информации о версиях GC см. Мой ответ на

    Определение того, какой сборщик мусора работает .

  4. Утилизация() немедленно высвобождает ресурсов , связанных с вашим объектом.Да, GC недетерминирован, но вызов Dispose () не вызывает GC!

  5. Dispose () сообщает ГХ, что вы закончили с этим объектом, и его память может быть восстановлена ​​в следующей коллекции для поколения, в котором этот объект живет .Если объект находится в Gen2 или в LOH, эта память не будет возвращена, если будет выполнена коллекция Gen0 или Gen1!

  6. Финализатор работает в 1 потоке (независимо от версииИспользуемый GC и количество логических процессоров на машине.Если вы много занимаетесь в очередях Finalization и f-достижимости, у вас есть только 1 поток, обрабатывающий все готовое для Finalization, ваша производительность падает, вы знаете, куда ...*

Информацию о том, как правильно реализовать IDisposable, см. В моем блоге:

Как правильно реализовать шаблон IDisposable?

4 голосов
/ 23 сентября 2011

Хорошо, время немного прояснить ситуацию (так как мой оригинальный пост был немного грязным).

IDisposable не имеет ничего общего с управлением памятью. IDisposable позволяет объекту очищать любые собственные ресурсы, которые он может удерживать. Если объект реализует IDisposable, вы должны обязательно либо использовать блок using, либо вызывать Dispose(), когда закончите с ним.

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

... так что, чтобы ответить на ваш вопрос, нет. Нет ничего плохого в том, чтобы зависеть от сборщика мусора .NET. Совсем наоборот.

1 голос
/ 13 ноября 2014

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

Как администратор БД, работавший с MS SQL, Oracle, Sybase / SAP и MYSQL, меня привлекли к работе над таинственной блокировкой и утечкой памяти, которая была обвинена в базе данных, когда на самом деле проблемабыло потому, что разработчик не закрывал и не разрушал их объекты соединения, когда они были сделаны с ними.Я даже видел приложения, которые оставляли незанятые соединения открытыми в течение нескольких дней, и это действительно может ухудшить ситуацию, когда ваша база данных кластеризована, зеркально отображена и всегда находится в группах восстановления в SQL Server 2012.

Когда я взял свой первый.Net class преподаватель учил нас держать открытыми соединения с базой данных, пока вы их используете.Садись, делай свою работу и уходи.Это изменение сделало несколько систем, которые я помогаю оптимизировать, намного более надежными.Это также освобождает память подключения в СУБД, предоставляя больше оперативной памяти для буферизации ввода-вывода.

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