Поиск по сайту:

Что такое сборка мусора и как она влияет на производительность вашей программы?


Сборка мусора — это функция многих языков, таких как C# и Java. В то время как ручное управление памятью, такое как C++, может быть довольно быстрым, автоматическая сборка мусора улучшает качество жизни разработчиков. Однако важно понимать последствия для производительности, если GC будет выполнять вашу работу.

Стек против кучи

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

Стек работает быстро и используется для типов значений с фиксированным размером байта. Конечно, это та же физическая память, что и куча, просто она быстрая, потому что это очень упорядоченная структура данных «первым пришел — последним вышел». Всякий раз, когда вы создаете локальную переменную, она сохраняет ее в стеке, и всякий раз, когда ваша функция завершает работу и переменная выходит за пределы области видимости, она автоматически удаляется из стека.

Это очень быстрый процесс, который делает выделение стека практически бесплатным. Несмотря на снижение производительности, это настолько дешево, насколько это возможно.

Куча, с другой стороны, используется для больших объектов, таких как списки и строки, которые слишком велики для хранения в стеке или должны храниться долгое время после того, как функции выходят за пределы области видимости. распределения не могут быть сделаны по замыслу. Всякий раз, когда вы выполняете new object, вы выделяете кучу. Вы также, вероятно, делаете выделение стека, поскольку, если вы сохраняете ссылку в локальную переменную, эта локальная переменная должна быть создана, чтобы указывать на фактическую память нового объекта.

Куча немного медленнее, как для выделения памяти, так и для ее удаления. Это относится ко всем языкам, использующим эту модель, со сборщиком мусора или без него.

Уборка мусора

Конечно, это не так просто, как «выделить один раз и забыть об этом». Если бы мы никогда не удаляли память, у нас возникла бы утечка памяти, что очень плохо и быстро съест оперативную память вашего компьютера. Выделения, такие как локальные списки, немедленно выйдут из области видимости, но без очистки навсегда засорят кучу. Таким образом, программы должны иметь способ очистки памяти, которая больше не нужна.

В языках ручного управления памятью, таких как C++, память обрабатывается вручную. Вы должны вручную освобождать память и удалять объекты, которые вы больше не используете, используя ссылку или указатель на расположение памяти этого объекта. Хотя это может быть очень быстро, программировать это неинтересно и может привести к ошибкам памяти и эксплойтам. Это одна из основных причин, по которой C++ считается «сложным» языком программирования для изучения и написания кода.

Альтернативой ручному управлению является то, что машина сделает это за вас автоматически. Это так называемая сборка мусора.

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

Например, возьмите следующий псевдокод, который создает и «удаляет» объект.

object refToObject = new object();
refToObject = null;

Поскольку refToObject больше не ссылается на созданный new object() , сборщик мусора увидит, что новый объект висит без ссылки на него откуда-либо, и удалит его. всякий раз, когда он собирает мусор в следующий раз.

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

Сборка мусора может быть запущена в любое время, обычно:

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

Влияние на производительность

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

Подумайте об этом так: ваш процессор может работать только над одной задачей за раз. С C++ он всегда работает над вашим кодом, включая биты, удаляющие память. С GC ваша программа не удаляет память и работает до тех пор, пока не создаст какой-нибудь мусор. Затем он приостанавливается, и ЦП переключается на работу по сборке мусора. Если он делает это часто, это может снизить производительность приложения.

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

  • Поколение 0, самое молодое поколение, содержащее недолговечные объекты, такие как временные переменные.
  • Поколение 1, которое действует как буфер между краткосрочными и долгосрочными объектами. Если объект пережил попытку сборки мусора, он будет «переведен» в более высокое поколение.
  • Поколение 2, последнее, которое отслеживает долгосрочные объекты.

GC будет проверять объекты в Gen0, затем в Gen1, затем в Gen2. Поскольку они содержат только временные или вновь созданные объекты, очистка Gen0 и Gen1 обычно выполняется довольно быстро, но Gen2 содержит много памяти. Выполнение «полной сборки мусора» может быть намного медленнее, чем эфемерная сборка мусора.

Как повысить производительность?

Итак, что вы можете сделать, чтобы предотвратить это? Ну, в конце концов, твой мусор надо убрать. Единственная реальная вещь, которую вы можете сделать, это уменьшить количество мусора, который выбрасывает ваша программа.

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

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

На практике это обычно делается с помощью универсального «пула объектов», который управляет списком объектов, которые он может «сдавать в аренду» вашей программе. Когда ваш код выполнен, он освобождает объект обратно в пул и сбрасывает его, готовый к повторному использованию по запросу.