В чем смысл сборщика мусора

SqlConnection connection = new SqlConnection(FROM_CONFIGURATION) 
SqlCommand command = new SqlCommand("SomeSQL", connection); 
connection.Open(); 
command.ExecuteNonQuery(); 
command.Dispose(); 
connection.Dispose();

Рекомендуется, чтобы приведенный выше код включал команду try / catch (или using), чтобы в случае возникновения исключения все ресурсы располагались должным образом.

Но если вам нужно беспокоиться об утилизации вещей вручную, тогда в чем смысл GC ?! Разве GC не позаботится об этом для кодировщика?

Хороший вопрос для тех, кто заинтересован в оптимизации своего кода.

Kon 21.10.2008 23:47

Как это связано с оптимизацией кода?

Scott Dorman 21.10.2008 23:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
687
15

Ответы 15

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

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

Chris Marasti-Georg 21.10.2008 23:52

But if you have to worry about disposing stuff manually, then what's the point of the GC?! Isn't GC there to take care of this for the coder?

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

... а некоторых ресурсов гораздо меньше, чем памяти.

Aaron Maenpaa 21.10.2008 23:41

Я не уверен насчет C#, как это выглядит, но обычно сборщик мусора управляет памятью. Это соединение, помимо объектной памяти, имеет ресурсы сервера. База данных, которая находится в отдельном процессе, должна поддерживать соединение. Закрытие очищает их.

GC действительно занимается утилизацией предметов, но утилизация может произойти не сразу. Удаление объектов вручную освободит память быстрее.

GC ограничен, когда дело доходит до освобождения внешних ресурсов, таких как подключения к БД или дескрипторы файлов. Однако для выделения памяти в мире .NET он берет на себя множество рутинных задач управления памятью.

Поскольку сборщик мусора не самый эффективный - это не всегда происходит, когда ресурс больше не используется. Поэтому, когда вы имеете дело с неуправляемыми ресурсами, такими как файловый ввод-вывод, соединения с БД и т. д., Лучше всего освободить / очистить эти ресурсы перед ожиданием и полагаться на сборщик мусора, который позаботится об этом.

И посмотрите на использование ключевого слова using:

using (SqlConnection connection = new SqlConnection(FROM_CONFIGURATION))
using (SqlCommand command = new SqlCommand("SomeSQL", connection))
{
  connection.Open(); 
  command.ExecuteNonQuery(); 
  command.Dispose(); 
  connection.Dispose();
}

Кроме того, как правило, все, что можно удалить, должно находиться в блоке using.

Если вы используете оператор using, вам не нужно явно вызывать метод Dipose (), это весь смысл использования 'using'.

IAmCodeMonkey 21.10.2008 23:51

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

Scott Dorman 21.10.2008 23:52

@ Скотт: Совершенно верно. Возможность отложить сборку мусора фактически делает более эффективным.

Jon Skeet 21.10.2008 23:59

@ Скотт: Ты прав, я просто не смог выразить свои мысли четко сформулированным образом. @IAmCodeMonkey: я никогда не говорил явно вызывать Dispose (). Я сказал использовать «использование» на всем одноразовом.

Kon 22.10.2008 00:05

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

gbjbaanb 22.10.2008 00:55

Объекты, реализующие IDisposable, пытаются сообщить вам, что у них есть связи со структурами, не управляемыми памятью. Сборщик мусора запускается партиями для повышения эффективности. Но это означает, что может пройти некоторое время, прежде чем ваш объект будет удален, а это означает, что вы будете удерживать ресурсы дольше, чем должны, что может отрицательно повлиять на производительность / надежность / масштабируемость.

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

Также я хочу добавить, что использование оператора using сделает это за вас очень хорошо.

Сборщик мусора запускается время от времени и заботится об управлении памятью, сохраняя для вас все в порядке и порядке. Вы можете подумать, что это бесполезно, когда вы видите фрагменты кода, подобные тому, который вы опубликовали, но чаще всего это избавляет вас от многих головных болей (подумайте о ручном управлении памятью C / C++), поскольку это значительно сокращает утечки памяти и позволяет вам беспокоиться о том, как будет работать ваше приложение, а не о том, как вы будете управлять памятью. Удаление дескрипторов файлов и соединений с базами данных - это способ повышения эффективности, поскольку сборка мусора не является детерминированной и может произойти не сразу, и вы не хотите, чтобы эти дескрипторы файлов и открытые соединения с базой данных снижали производительность вашей системы. Кстати, ваш код действительно уродлив, я всегда защищаю оператор using и часто пишу свой код db следующим образом:

using (SqlConnection connection = new SqlConnection(...))
using (SqlCommand command = connection.CreateCommand())
{
   ...
}

Это автоматически вызывает dispose для объектов соединения и команд, когда они выпадают из области видимости, то есть когда выполнение покидает блок.

C++ ручное управление памятью? Чувак, это как 80-е, ты никогда не слышал о RAII?

gbjbaanb 22.10.2008 00:52

Сборщик мусора (GC) в .NET является основной частью .NET Common Language Runtime (CLR) и доступен для всех языков программирования .NET. GC никогда не предназначался для управления ресурсами; он был разработан для управления распределением памяти и отлично справляется с управлением памятью, выделенной непосредственно для собственных объектов .NET. Он не предназначен для работы с неуправляемой памятью и памятью, выделенной операционной системой, поэтому ответственность за управление этими ресурсами ложится на разработчика.

В конкретном случае соединений с базой данных вы имеете дело не только с памятью, но и с другими ресурсами, а именно с пулами соединений, возможными неявными областями транзакций и т. д. Вызывая Close () и / или Dispose (), вы явно указываете объекту освободить эти неуправляемые ресурсы немедленно, в то время как управляемые ресурсы будут ждать выполнения цикла сборки мусора.

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

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

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

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

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

Другие люди могут продолжать так часами.

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

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

Предположим, у меня есть такой код:

class MonkeyGrabber : IDisposable {
   public MonkeyGrabber() { /* construction grabs a real, live monkey from the cage */
   public void Dispose() { Dispose(true); /* releases the monkey back into the cage */ }
   // the rest of the monkey grabbing is left as an exercise to grad student drones
}

class MonkeyMonitor {
    public void CheckMonkeys() {
        if (_monkeyPool.GettingTooRowdy()) {
            MonkeyGrabber grabber = new MonkeyGrabber();
            grabber.Spank();
        }
    }
}

Теперь мой MonkeyMonitor проверяет обезьян, и если они слишком хулиганят, он получает ценный системный ресурс - единственную обезьяну, хватающую коготь, прикрепленную к системе, и использует ее, чтобы схватить обезьяну и отшлепать ее. Поскольку я не утилизировал его, обезьяна все еще держится за обезьяну, которая болтается над клеткой для отдыха. Если остальные обезьяны продолжат хулиганить, я не смогу сделать новый MonkeyGrabber, так как он все еще помогает. Ой. Надуманный пример, но вы уловили суть: объекты, реализующие IDisposable, могут удерживать ограниченные ресурсы, которые следует своевременно освобождать. GC может в конце концов отпустить или нет.

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

Подробнее об IDisposable.

использование - твой друг - это ближайший к нам на данный момент RAII.

...The GC was never meant to manage resources; it was designed to manage memory allocation ... In the specific case of database connections, you are dealing with other resources than just memory... (Scott Dorman)

OP не помечал конкретную платформу, хотя большинство ответов были специфичными для .net, отмечая, что GC в основном предназначен для предотвращения утечек памяти, но расширения, такие как выражения using и IDisposable, могут очень помочь.

Другие платформы предлагают другие решения. Например, в C++ нет (встроенной) сборки мусора, но некоторые формы общих указателей могут использоваться для помощи в управлении памятью, а кодирование в стиле RAII может быть чрезвычайно полезным при управлении другими типами ресурсов.

В cPython используются две разные системы сборки мусора. Реализация подсчета ссылок немедленно вызывает деструкторы при удалении последней ссылки. Для обычных объектов «стека» это означает, что они немедленно очищаются, как это происходит с объектами C++ RAII. Обратной стороной является то, что если у вас есть цикл ссылок, сборщик подсчета ссылок никогда не избавится от объекта. В результате у них есть вторичный недетерминированный сборщик мусора, который работает как сборщики Java и .NET. Подобно .NET с его операторами using, cPython пытается обрабатывать наиболее распространенные случаи.

Итак, чтобы ответить на OP, недетерминированная сборка мусора помогает упростить управление памятью, ее можно использовать и для обработки других ресурсов, если своевременность не является проблемой, и другой механизм (например, тщательное программирование, сборщик мусора с подсчетом ссылок, сборщик мусора). using или реальные объекты RAII) необходимы, когда необходимо своевременное высвобождение других ресурсов.

А теперь я понимаю. Я перепутал управление памятью с неуправляемым управлением ресурсами. Благодарю за разъяснение!

Другие вопросы по теме