Освобождение памяти и сборка мусора net приложений
Примеры
В следующем примере используется несколько методов сборки мусора для получения сведений о создании и памяти о блоке неиспользуемых объектов и его печати в консоли. Затем собираются неиспользуемые объекты, а результирующие итоги памяти отображаются.
Эфемерные поколения и сегменты
Так как объекты в поколениях 0 и 1 являются короткоживущими, эти поколения называются эфемерными поколениями.
Эфемерные поколения выделяются в сегменте памяти, который называется эфемерным сегментом. Каждый новый сегмент, полученный сборщиком мусора, становится новым эфемерным сегментом и содержит объекты, пережившие сборку мусора для поколения 0. Старый эфемерный сегмент становится новым сегментом поколения 2.
Размер эфемерного сегмента зависит от того, является ли система 32- или 64-разрядной, и от типа сборщика мусора (сборка мусора рабочей станции или сервера). В следующей таблице показаны размеры эфемерного сегмента по умолчанию.
Сборка мусора рабочей станции и сервера | 32-разрядная версия | 64-разрядная версия |
---|---|---|
Сборщик мусора рабочей станции | 16 МБ | 256 МБ |
Сборщик мусора сервера | 64 МБ | 4 Гбайт |
GC сервера с > 4 логическими процессорами | 32 МБ | 2 ГБ |
GC сервера с > 8 логическими ЦП | 16 МБ | 1 ГБ |
Этот эфемерный сегмент может содержать объекты поколения 2. Объекты поколения 2 могут использовать несколько сегментов (столько, сколько требуется процессу и сколько разрешает память).
Объем памяти, освобождаемой при эфемерной сборке мусора, ограничен размером эфемерного сегмента. Освобождаемый объем памяти пропорционален пространству, занятому неиспользуемыми объектами.
Условия для сборки мусора
Сборка мусора возникает при выполнении одного из следующих условий:
Объем памяти, используемой объектами, выделенными в управляемой куче, превышает допустимый порог. Этот порог непрерывно корректируется во время выполнения процесса.
вызывается метод GC.Collect . Практически во всех случаях вызов этого метода не потребуется, так как сборщик мусора работает непрерывно. Этот метод в основном используется для уникальных ситуаций и тестирования.
Поколения сборщика мусора
Классы с финализаторами также могут нарушить бесперебойную работу сборщика мусора. Объекты этих классов не могут быть удалены немедленно, вместо этого они попадают в очередь финализаторов и удаляются из памяти только после того, как финализатор будет выполнен. Это означает, что любой объект, на который они ссылаются (и любой объект, на который ссылаются эти объекты, и так далее), должен храниться в памяти как минимум до момента вызова финализатора, и потребуется как минимум две сборки мусора, прежде чем память снова станет доступной. Если граф содержит много объектов с финализаторами, это может означать, что сборщику мусора потребуется много проходов, чтобы полностью освободить и удалить ненужные объекты.
Существует простой способ избежать этой проблемы — реализуйте интерфейс IDisposable на финализируемых классах, перенесите необходимые для финализации объекта действия в метод Dispose() , в конце которого вызовите GC.SuppressFinalize() . Финализатор затем может быть модифицирован так, чтобы в нем вызывался метод Dispose() . GC.SuppressFinalize() сообщает сборщику мусора, что объект больше не нуждается в финализации и может быть немедленно удален, в результате чего память будет освобождена гораздо быстрее.
Производительность сборщика мусора
С точки зрения производительности, наиболее важной особенностью систем с автоматической сборкой мусора является то, что сборщик может начать выполнение в любое время. Это делает такие системы непригодными для использования в случаях, когда время выполнения критически важно, поскольку любая операция может быть приостановлена работой сборщика мусора.
Граф объектов
Все становится еще более запутанным, когда в игру вступают циклические ссылки.
Граф связей между объектами и корнями (корни обозначены как "GC root")
При разработке приложений программистам часто удобнее представлять память организованной в дерево, начинающееся с отдельных корней:
Древовидное представление связей относительно корня GC root 2
Профилировщик памяти позволяет взглянуть на граф иначе — как на дерево, начинающееся с какого-либо корневого объекта (не следует путать корни сборщика мусора и корневые объекты дерева). Следуя по ссылкам, указывающим на объекты дерева начиная с корневого (т. е. в обратном направлении), мы можем поместить в его листья все корни сборщика мусора. Например, начиная с объекта ClassC , на который ссылается корень GC root 2, мы можем проследить все ссылки и получить следующий граф:
Древовидное представление связей относительно объекта ClassC
Таким образом мы увидим, что объект ClassC имеет двух корней-владельцев, оба из которых должны перестать на него ссылаться, прежде чем сборщик мусора сможет его удалить. Чтобы объект ClassC был удален после того, как корень GC root 2 будет установлен в null , должна быть разорвана любая из промежуточных связей между корнем GC root 3 и объектом.
Реальные приложения, особенно с компонентами пользовательского интерфейса, имеют гораздо более сложные графы, чем в примерах выше. Даже на такую простую вещь, как label в диалоговом окне, можно ссылаться из огромного количества различных мест:
Пример древовидного представления связей относительно объекта в реальном проекте
В таком лабиринте может запросто потеряться какой-нибудь объект.
Комментарии
Сборщик мусора — это компонент среды CLR, который управляет выделением и освобождением управляемой памяти. Методы этого класса влияют на то, когда сборка мусора выполняется для объекта и когда освобождаются ресурсы, выделенные объектом. Свойства этого класса предоставляют сведения об общем объеме памяти, доступном в системе, а также возрастной категории или поколении памяти, выделенной для объекта.
Сборщик мусора отслеживает и освобождает объекты, выделенные в управляемой памяти. Периодически сборщик мусора выполняет сборку мусора для освобождения памяти, выделенной объектам, для которых нет допустимых ссылок. Сборка мусора выполняется автоматически, когда запрос на использование памяти не может быть удовлетворен с помощью доступной свободной памяти. Кроме того, приложение может принудительно выполнить сборку мусора Collect с помощью метода.
Сборка мусора состоит из следующих шагов:
Сборщик мусора выполняет поиск управляемых объектов, на которые ссылается управляемый код.
Сборщик мусора пытается завершить работу объектов, на которые не ссылаются.
Сборщик мусора освобождает объекты, на которые не ссылаются, и освобождает память.
Этот раздел включает следующие подразделы:
Запрет сборки мусора
Вы определяете начало критического пути без региона сборки мусора путем вызова одной из перегрузок.TryStartNoGCRegion Чтобы указать конец критического пути, вызовите EndNoGCRegion метод.
Вы не можете вложить вызовы метода TryStartNoGCRegion , и его следует вызывать EndNoGCRegion только в том случае, если среда выполнения в настоящее время не находится в режиме задержки в регионе сборки мусора. Другими словами, не следует вызывать TryStartNoGCRegion несколько раз (после первого вызова метода последующие вызовы не будут выполнены) и не следует ожидать, что вызовы EndNoGCRegion будут выполнены успешно, так как первый вызов TryStartNoGCRegion выполнен успешно.
Сборщик мусора и неуправляемые ресурсы
Во время сборки сборщик мусора не освобождает объект, если он находит одну или несколько ссылок на объект в управляемом коде. Однако сборщик мусора не распознает ссылки на объект из неуправляемого кода и может освободить объекты, которые используются исключительно в неуправляемом коде, если это не было явно запрещено. Этот KeepAlive метод предоставляет механизм, который не позволяет сборщику мусора собирать объекты, которые по-прежнему используются в неуправляемом коде.
Помимо выделения управляемой памяти реализация сборщика мусора не поддерживает сведения о ресурсах, удерживаемых объектом, таких как дескрипторы файлов или подключения к базе данных. Если тип использует неуправляемые ресурсы, которые должны быть освобождены до освобождения экземпляров типа, тип может реализовать метод завершения.
В сценариях, когда ресурсы должны быть освобождены в определенное время, классы могут реализовать IDisposable интерфейс, который содержит IDisposable.Dispose метод, выполняющий задачи управления ресурсами и очистки. Классы, реализующие, Dispose должны указывать в рамках контракта класса, если и когда потребители класса вызывают метод для очистки объекта. Сборщик мусора по умолчанию не вызывает Dispose метод. Однако реализации Dispose метода могут вызывать методы в GC классе для настройки поведения завершения сборщика мусора.
Дополнительные сведения о завершении объектов и шаблоне удаления см. в разделе "Очистка неуправляемых ресурсов".
Заключение
Примечание переводчика
Фрагментация кучи
Менее известное ограничение — это куча больших объектов (Large Object Heap, LOH), в которой размещаются объекты размером от 85000 байт. Куча больших объектов никогда не уплотняется, соответственно, объекты, которые в ней размещены, никогда не перемещаются, что может привести к преждевременному исчерпанию памяти в программе. Когда одни объекты живут дольше других, в куче образуются так называемые дыры — это называется фрагментацией. Проблема возникает, когда программа запрашивает большой блок памяти, но куча стала настолько фрагментированной, что в ней нет ни одной непрерывной области, достаточно большой, чтобы вместить его. Исключение OutOfMemoryException , вызванное фрагментацией, обычно происходит, когда программа имеет много свободной памяти, но из-за фрагментации не может разместить в ней новый объект.
Другим симптомом фрагментации является то, что .NET-приложению приходится держать память, "занятую" пустыми дырами. Это приводит к тому, что при просмотре в диспетчере задач кажется, что приложение использует гораздо больше памяти, чем ему нужно. Именно это часто происходит, когда профилировщик показывает, что выделенные программой объекты используют лишь небольшой объем памяти, а диспетчер задач показывает, что процесс занимает большой объем.
Процесс сборки мусора
Сборка мусора состоит из следующих этапов:
Этап маркировки, выполняющий поиск всех используемых объектов и составляющий их перечень.
Этап перемещения, обновляющий ссылки на сжимаемые объекты.
Этап сжатия, освобождающий пространство, занятое неиспользуемыми объектами и сжимающий выжившие объекты. На этапе сжатия объекты, пережившие сборку мусора, перемещаются к более старому концу сегмента.
Так как сборки поколения 2 могут занимать несколько сегментов, объекты, перешедшие в поколение 2, могут быть перемещены в более старый сегмент. Выжившие объекты поколений 1 и 2 могут быть перемещены в другой сегмент, так как они перешли в поколение 2.
- Предельный объем памяти для контейнера.
- Параметры конфигурации среды выполнения гчеафардлимит или гчеафардлимитперцент .
Чтобы определить, являются ли объекты используемыми, сборщик мусора задействует следующие сведения.
Корни стека. Переменные стека, предоставленные JIT-компилятором и средством обхода стека. JIT-оптимизация позволяет уменьшить или увеличить области кода, в которых переменные стека сообщаются сборщику мусора.
Дескрипторы сборки мусора. Дескрипторы, которые указывают на управляемые объекты и которые могут быть выделены пользовательским кодом или средой CLR.
Статические данные. Статические объекты в доменах приложений, которые могут ссылаться на другие объекты. Каждый домен приложения следит за своими статическими объектами.
Перед запуском сборки мусора все управляемые потоки, кроме потока, запустившего сборку мусора, приостанавливаются.
На следующем рисунке показан поток, запускающий сборку мусора и вызывающий приостановку других потоков.
Поколения
Алгоритм сборки мусора учитывает следующее:
- Уплотнять память для части управляемой кучи быстрее, чем для всей кучи.
- У новых объектов время жизни меньше, а старых больше.
- Новые объекты теснее связаны друг с другом, и приложение обращается к ним приблизительно в одно и то же время.
Сборка мусора в основном сводится к уничтожению короткоживущих объектов с небольшим временем жизни. Для оптимизации производительности сборщика мусора управляемая куча делится на три поколения: 0, 1 и 2. Следовательно, объекты с большим и небольшим временем жизни обрабатываются отдельно. Сборщик мусора хранит новые объекты в поколении 0. Уровень объектов, созданных на раннем этапе работы приложения и оставшихся после сборок мусора, повышается, и они сохраняются в поколении 1 и 2. Так как сжать часть управляемой кучи быстрее, чем всю кучу, эта схема позволяет сборщику мусора освобождать память в определенном поколении, а не для всей кучи при каждой сборке мусора.
Поколение 0. Это самое молодое поколение содержит короткоживущие объекты. Примером короткоживущего объекта является временная переменная. Сборка мусора чаще всего выполняется в этом поколении.
Вновь распределенные объекты образуют новое поколение объектов и неявно являются сборками поколения 0. Однако если это большие объекты, то они попадают в кучу больших объектов, которая иногда называется поколением 3. Поколение 3 — это физическое поколение, которое логически собирается как часть поколения 2.
Большинство объектов уничтожается при сборке мусора для поколения 0 и не доживает до следующего поколения.
Если приложение пытается создать новый объект, когда поколение 0 заполнено, сборщик мусора выполняет сбор, чтобы попытаться освободить адресное пространство для объекта. Сборщик мусора начинает проверять объекты в поколении 0, а не все объекты в управляемой куче. Сборка мусора только в поколении 0 зачастую освобождает достаточно памяти для того, чтобы приложение могло и дальше создавать новые объекты.
Поколение 1. Это поколение содержит коротко живущие объекты и служит буфером между короткоживущими и долгоживущими объектами.
Когда сборщик мусора выполняет сборку для поколения 0, память уплотняется для достижимых объектов и они продвигаются в поколение 1. Так как объекты, оставшиеся после сборки, обычно склонны к долгой жизни, имеет смысл продвинуть их в поколение более высокого уровня. Сборщику мусора необязательно выполнять повторную проверку объектов поколений 1 и 2 при каждой сборке мусора поколения 0.
Если сборка поколения 0 не освобождает достаточно памяти, чтобы приложение могло создать новый объект, сборщик мусора может выполнить сборку мусора поколения 1, а затем поколения 2. Объекты в поколении 1, оставшиеся после сборок, продвигаются в поколение 2.
Поколение 2. Это поколение содержит долгоживущие объекты. Примером долгоживущих объектов служит объект в серверном приложении, содержащий статические данные, которые существуют в течение длительности процесса.
Объекты в поколении 2, оставшиеся после сборки, находятся там до тех пор, пока они не станут недостижимыми в следующей сборке.
Объекты в куче больших объектов (иногда называемой поколением 3) также собираются в поколении 2.
Сборки мусора выполняются для конкретных поколений при выполнении соответствующих условий. Сборка поколения означает сбор объектов в этом поколении и во всех соответствующих младших поколениях. Сборка мусора поколения 2 также называется полной сборкой, так как при этом уничтожаются объекты во всех поколениях (то есть все объекты в управляемой куче).
Неиспользуемые объекты, на которые все еще есть ссылки
Источник таких утечек бывает довольно трудно обнаружить, хотя симптомы очевидны — рост потребления памяти. Для начала, необходимо определить, какие неиспользуемые объекты остаются в памяти, а затем отследить ссылки, ведущие на них, чтобы выяснить, почему объекты не удаляются. Для решения этой задачи необходим профилировщик памяти. Сравнивая снимки памяти во время утечки, можно найти проблемные неиспользуемые объекты, но отследить ссылки на них в обратном направлении не сможет ни один отладчик.
Выживание и переходы
Объекты, которые не уничтожаются при сборке мусора, называются выжившими объектами и переходят в следующее поколение.
- Объекты, оставшиеся после сборки мусора поколения 0, подвигаются в поколение 1.
- Объекты, оставшиеся после сборки мусора поколения 1, подвигаются в поколение 2.
- Объекты, оставшиеся после сборки мусора поколения 2, остаются в поколении 2.
Когда сборщик мусора обнаруживает высокую долю выживания в поколении, он повышает порог распределений для этого поколения. При следующей сборке мусора освобождается заметная часть занятой памяти. В среде CLR непрерывно контролируется равновесие двух приоритетов: не позволить рабочему набору приложения стать слишком большим, задерживая сборку мусора, и не позволить сборке мусора выполняться слишком часто.
Режимы работы сборщика мусора
Режим сборки мусора можно задать в конфигурационном файле приложения, если значение по умолчанию не подходит. Выбор непараллельного режима сборки может быть полезен, когда важнее, чтобы приложение имело высокую пропускную способность, а не выглядело отзывчивым.
Как работает сборщик мусора
Так как же все-таки работает магия сборщика мусора? Основная идея довольно проста — он изучает, как объекты размещены в памяти, определяя те из них, до которых может добраться запущенная программа, следуя некоторой цепочке ссылок.
Когда начинается сборка мусора, сборщик просматривает набор ссылок, называемых корнями. Это участки памяти, которые в силу определенных причин должны быть доступны всегда, и которые содержат ссылки на объекты, созданные программой. Сборщик помечает эти объекты как живые, а затем просматривает все объекты, на которые они ссылаются, помечая живыми и их. Сборщик мусора продолжает в том же духе, пока не пометит живыми все объекты, которые он смог найти таким способом.
Сборщик мусора определяет объект как ссылающийся на другой объект, если он или один из его предков имеет поле, содержащее ссылку на другой объект.
Локальные переменные ссылочного типа в методе, который выполняется в данный момент.
Объекты, на которые ссылаются эти переменные, всегда должны быть немедленно доступны методу, в котором они объявлены, поэтому их необходимо хранить. Время жизни таких корней может зависеть от того, как была собрана программа. В отладочных сборках локальная переменная живет до тех пор, пока метод находится в стеке. В релизных сборках оптимизирующий JIT-компилятор может посмотреть на структуру программы, чтобы определить последнюю точку, когда переменная используется методом, и удалить ее, когда она больше не нужна. Эта стратегия используется не всегда — ее можно отключить, например, запустив программу в отладчике.
Управляемые объекты, переданные в неуправляемую библиотеку через Interop.
Если управляемый объект передается в неуправляемую библиотеку COM+ через Interop, то он станет корневым объектом с подсчетом ссылок. Это происходит потому, что COM+ не выполняет сборку мусора. Вместо этого он использует систему подсчета ссылок. Как только библиотека COM+ завершает работу с объектом, устанавливая счетчик ссылок в 0, он перестает быть корневым и может быть удален.
Ссылки на объекты с финализатором.
Старение объектов и поколения
Сборщик мусора в среде CLR поддерживает старение объектов с помощью поколений. Поколение — это единица измерения относительного возраста объектов в памяти. Номер поколения или возраст объекта указывает на поколение, к которому принадлежит объект. Объекты, созданные в последнее время, являются частью новых поколений и имеют более низкие числа поколений, чем объекты, созданные ранее в жизненном цикле приложения. Объекты в последнем поколении находятся в поколении 0. Эта реализация сборщика мусора поддерживает три поколения объектов, поколений 0, 1 и 2. Значение свойства можно получить, чтобы определить максимальное число MaxGeneration поколения, поддерживаемое системой.
Старение объектов позволяет приложениям ориентироваться на сборку мусора в определенном наборе поколений, а не требовать от сборщика мусора для оценки всех поколений. Перегрузки Collect метода, включающего generation параметр, позволяют указать самое старое поколение для сбора мусора.
Ограничения сборщика мусора
Управляемая куча
После инициализации средой CLR сборщик мусора выделяет сегмент памяти для хранения объектов и управления ими. Эта память называется управляемой кучей в отличие от собственной кучи операционной системы.
Управляемая куча создается для каждого управляемого процесса. Все потоки в процессе выделяют память для объектов в одной и той же куче.
Для резервирования памяти сборщик мусора вызывает функцию Windows VirtualAlloc и резервирует для управляемых приложений по одному сегменту памяти за раз. Сборщик мусора также резервирует сегменты по мере необходимости и возвращает операционной системе освобожденные сегменты (очистив их от всех объектов), вызывая функцию Windows VirtualFree.
Размер сегментов, выделенных сборщиком мусора, зависит от реализации и может быть изменен в любое время, в том числе при периодических обновлениях. Приложение не должно делать никаких допущений относительно размера определенного сегмента, полагаться на него или пытаться настроить объем памяти, доступный для выделения сегментов.
Чем меньше объектов распределено в куче, тем меньше придется работать сборщику мусора. При размещении объектов не используйте округленные значения, превышающие фактические потребности, например не выделяйте 32 байта, когда необходимо только 15 байтов.
Активированный процесс сборки мусора освобождает память, занятую неиспользуемыми объектами. Процесс освобождения сжимает используемые объекты, чтобы они перемещались вместе, и удаляет пространство, занятое неиспользуемыми объектами, уменьшая, таким образом, кучу. Это гарантирует, что объекты, распределенные совместно, останутся в управляемой куче рядом, чтобы сохранить локальность.
Степень вмешательства (частота и длительность) сборок мусора зависит от числа распределений и сохранившейся в управляемой куче памяти.
Кучу можно рассматривать как совокупность двух куч: куча больших объектов и куча маленьких объектов. Куча больших объектов содержит объекты размером от 85 000 байтов, обычно представленные массивами. Экземпляр объекта редко бывает очень большим.
Вы можете настроить пороговый размер для объектов, помещаемых в кучу больших объектов.
Управление свободным списком
Управление на основе списка свободных блоков - это механизм управления распределением памяти в стандартной библиотеке языка C, который также по умолчанию используется функциями управления памятью в C++, такими как new и delete. Это детерминированный диспетчер памяти, при использовании которого вся ответственность за выделение и освобождение памяти ложится на плечи разработчика. Свободные блоки памяти хранятся в виде связанного списка, откуда изымаются блоки памяти при выделении, и куда они возвращаются, при освобождении.
Он изымает блоки из списка при выделении памяти и возвращает их обратно при освобождении. Приложение обычно получает блоки памяти, хранящие их размеры в служебной области.
Механизм управления памятью на основе списка не свободен от тактических и стратегических решений, влияющих на производительность приложения. Ниже перечислены некоторые из них:
Приложение, использующее механизм управления памятью на основе списка свободных блоков, изначально получает небольшой пул свободных блоков, организованных в виде списка. Список может быть отсортирован по размеру, по времени использования и так далее.
Когда диспетчер получает от приложения запрос на выделение памяти, он выполняет поиск соответствующего блока памяти. Соответствие может определяться по принципу «первый подходящий», «лучше подходящий» или с применением более сложных критериев.
После исчерпания списка диспетчер запрашивает у операционной системы дополнительные свободные блоки и добавляет их в список. Когда приложение возвращает память диспетчеру, он добавляет освободившийся блок в список. На этом этапе может выполняться слияние смежных свободных блоков, дефрагментация и сокращение списка, и так далее.
Ниже перечислены основные проблемы, связанные с управлением памятью на основе списка свободных блоков:
Высокая стоимость операции выделения: поиск блока, соответствующего параметрам запроса, требует времени, даже при использовании критерия «первый подходящий». Кроме того, блоки часто разбиваются на несколько частей. В многопроцессорных системах неизбежно возникает конкуренция за список и необходимость синхронизации операций, если только не используется несколько списков. С другой стороны, наличие нескольких списков ухудшает их фрагментацию.
Высокая стоимость освобождения: возврат блока в список требует времени, и здесь снова возникает проблема синхронизации конкурирующих операций освобождения памяти.
Высокая стоимость управления: чтобы избежать ситуации отсутствия блоков памяти подходящего размера при наличии большого количества маленьких блоков, необходимо выполнять дефрагментацию списка. Но эта работа должна производиться в отдельном потоке выполнения, что опять же требует применения блокировок для доступа к списку и снижает скорость операций выделения и освобождения памяти. Фрагментацию можно уменьшить, выделяя блоки фиксированного размера и поддерживая несколько списков, но при этом увеличивается количество операций по поддержанию динамической памяти в целостном состоянии и добавляет накладные расходы к каждой операции выделения и освобождения памяти.
Назначение сборщика мусора
Сборка мусора - это высокоуровневая абстракция, избавляющая разработчиков от необходимости заботиться об освобождении управляемой памяти. В окружениях, снабженных механизмом сборки мусора, выделение памяти производится в момент создания объектов, а освобождение происходит, когда в программе исчезает последняя ссылка на объект. Кроме того, сборщик мусора предоставляет интерфейс финализации для неуправляемых ресурсов, находящихся за пределами управляемой динамической памяти, благодаря чему имеется возможность обеспечить выполнение кода, когда эти ресурсы окажутся не нужны.
избавиться от ошибок и ловушек, связанных с управлением памятью вручную;
обеспечить производительность операций управления памятью, равную или превышающую производительность ручных механизмов.
В существующих языках программирования и фреймворках используются различные стратегии управления памятью. Мы коротко исследуем две из них: управление на основе списка свободных блоков (реализацию которой можно найти в коллекции стандартных инструментов управления памятью языка C) и сборка мусора на основе подсчета ссылок.
Методы
Информирует среду выполнения о выделении большого объема неуправляемой памяти, которую необходимо учесть при планировании сборки мусора.
По возможности выделяет массив при пропуске нулевой инициализации.
Отменяет регистрацию уведомления о сборке мусора.
Принудительно запускает немедленную сборку мусора для всех поколений.
Принудительно начинает немедленную сборку мусора, начиная с нулевого поколения и вплоть до указанного поколения.
Принудительно запускает немедленную сборку мусора начиная с нулевого поколения и вплоть до указанного поколения в момент времени, заданный значением GCCollectionMode.
Принудительная сборка мусора с поколения 0 до указанного поколения во время, указанное значением GCCollectionMode, со значением, указывающим, должна ли сборка быть блокирующей.
Принудительная сборка мусора с поколения 0 до указанного поколения во время, указанное значением GCCollectionMode, со значениями, указывающими, должна ли сборка быть блокирующей и сжимающей.
Возвращает количество операций сборки мусора, выполненных для заданного поколения объектов.
Завершает режим задержки без области сборки мусора.
Возвращает общее число байтов, выделенных для текущего потока с начала времени его существования.
Возвращает сведения о памяти при сборке мусора.
Возвращает сведения о памяти при сборке мусора.
Возвращает номер текущего поколения указанного объекта.
Возвращает текущий номер поколения для целевого объекта указанной слабой ссылки.
Возвращает число байтов, выделенных за время существования процесса. Возвращаемое значение не включает собственные выделения.
Извлекает предполагаемое количество выделенных в данный момент байтов. Параметр указывает, может ли этот метод выдержать короткий интервал времени ожидания перед возвратом, пока система выполняет сборку мусора и завершает объекты.
Ссылается на указанный объект, делая его недоступным для сборщика мусора с момента начала текущей процедуры до вызова этого метода.
Указывает, что необходимо отправлять уведомления о сборке мусора, когда соблюдены условия для полной сборки мусора и когда завершена сборка.
Информирует среду выполнения о том, что неуправляемая память освобождена и ее более не требуется учитывать при планировании сборки мусора.
Требует, чтобы система вызвала метод завершения для указанного объекта, для которого ранее был вызван метод SuppressFinalize(Object).
Сообщает среде CLR, что она на не должна вызывать метод завершения для указанного объекта.
Пытается запретить сборку мусора во время выполнения критического пути, если доступен указанный достаточный объем памяти.
Пытается запретить сборку мусора во время выполнения критического пути, если доступен указанный объем памяти, и устанавливает, будет ли выполняться полная блокирующая сборка мусора, если изначально не хватает памяти.
Пытается запретить сборку мусора во время выполнения критического пути, если указанный объем памяти доступен для кучи больших объектов и для кучи маленьких объектов.
Пытается запретить сборку мусора во время выполнения критического пути, если доступен указанный объем памяти для кучи больших объектов и для кучи маленьких объектов, и устанавливает, будет ли выполняться полная блокирующая сборка мусора, если изначально не хватает памяти.
Приостанавливает текущий поток до тех пор, пока поток, обрабатывающий очередь методов завершения, не обработает всю очередь.
В среде CLR сборщик мусора выполняет функции автоматического диспетчера памяти. Сборщик мусора управляет выделением и освобождением памяти для приложения. Следовательно, разработчикам, работающим с управляемым кодом, не нужно писать код для выполнения задач по управлению памятью. Автоматическое управление памятью позволяет устранить распространенные проблемы, которые связаны с утечкой памяти из-за того, что объект не был освобожден, или попыткой доступа к памяти для объекта, который был освобожден.
В этой статье описаны основные понятия сборки мусора.
Освобождение памяти
Механизм оптимизации сборщика мусора определяет наилучшее время для выполнения сбора, основываясь на произведенных выделениях памяти. Когда сборщик мусора выполняет очистку, он освобождает память, выделенную для объектов, которые больше не используются приложением. Он определяет, какие объекты больше не используются, анализируя корни приложения. Корни приложения содержат статические поля, локальные переменные в стеке потока, регистры процессора, дескрипторы сборки мусора и очередь завершения. Каждый корень либо ссылается на объект, находящийся в управляемой куче, либо имеет значение NULL. Сборщик мусора может запросить остальную часть среды выполнения для этих корней. С помощью этого списка он проверяет корни приложения и при этом создает граф, содержащий все объекты, к которым можно получить доступ из этих корней.
Объекты, не входящие в этот граф, являются недостижимыми из данных корней приложения. Сборщик мусора считает недостижимые объекты мусором и освобождает выделенную для них память. В процессе очистки сборщик мусора проверяет управляемую кучу, отыскивая блоки адресного пространства, занятые недостижимыми объектами. При обнаружении недостижимого объекта он использует функцию копирования памяти для уплотнения достижимых объектов в памяти, освобождая блоки адресного пространства, выделенные под недостижимые объекты. После уплотнения памяти, занимаемой достижимыми объектами, сборщик мусора вносит необходимые поправки в указатель, чтобы корни приложения указывали на новые расположения объектов. Он также устанавливает указатель управляемой кучи в положение после последнего достижимого объекта.
Память уплотняется, только если при очистке обнаруживается значительное число недостижимых объектов. Если после сборки мусора все объекты в управляемой куче остаются на месте, то уплотнение памяти не требуется.
Для повышения производительности среда выполнения выделяет память для больших объектов в отдельной куче. Сборщик мусора автоматически освобождает память, выделенную для больших объектов. Но для устранения перемещений в памяти больших объектов эта память обычно не сжимается.
Неуправляемые ресурсы
Для большинства объектов, созданных приложением, сборщик мусора автоматически выполнит необходимые задачи по управлению памятью. Однако для неуправляемых ресурсов требуется явная очистка. Основным типом неуправляемых ресурсов являются объекты, образующие упаковку для ресурсов операционной системы, такие как дескриптор файлов, дескриптор окна или сетевое подключение. Хотя сборщик мусора может отслеживать время жизни управляемого объекта, инкапсулирующего неуправляемый ресурс, он не знает, как освобождать эти ресурсы.
При создании объекта, инкапсулирующего неуправляемый ресурс, рекомендуется предоставлять необходимый код для очистки неуправляемого ресурса в общем методе Dispose . Предоставление метода Dispose дает возможность пользователям объекта явно освобождать память при завершении работы с объектом. Когда используется объект, инкапсулирующий неуправляемый ресурс, вызовите Dispose при необходимости.
Кроме того, нужно предусмотреть способ освобождения неуправляемых ресурсов в случае, если потребитель типа не вызовет Dispose . Вы можете использовать защищенный обработчик для создания оболочки для неуправляемого ресурса или переопределить метод Object.Finalize().
Базовые сведения о времени жизни объекта
После создания объект будет автоматически удалён сборщиком мусора, когда в нём отпадёт надобность. Сразу возникает вопрос о том, каким образом сборщик мусора определяет, когда в объект отпадает необходимость?
Давайте просмотрим простой пример:
Обратите внимание, что ссылка на объект Car(myCar) была создана непосредственно внутри метода MakeACar() и не передавалась за пределы определяющей области действия (ни в виде возвращающегося значения, ни в виде параметров ref/out). По этому после вызова метода ссылка на myCar окажется недостижимой, а объект Car — кандидатом на удаление сборщиком мусора. Следует, однако, понимать, что нет никакой гарантии на удаление объекта сразу после выполнение метода MakeACar(). Всё, что в данный момент можно предполагать, так это то, что когда в CLR-среде будет в следующий раз проводиться сборка мусора, то объект myCar будет поставлен на удаление.
Программистам на C++ хорошо известно, что если они специально не позаботятся об удалении размещённых в куче объектов, вскоре обязательно начнут возникать «утечки памяти». На самом деле отслеживание проблем, связанных с проблемой утечки памяти, являются одним из самых утомительных и длинных аспектов программирования в неуправляемых средах.
Роль корневых элементов приложения
Во время процессы сборки мусора исполняющая среда будет исследовать объекты в куче, чтобы определить, являются ли они по прежнему достижимыми (т.е. корневыми) для приложения. Для этого среда CLR будет создавать графы объектов, представляющие все достижимые для приложения объекты. Кроме того, следует иметь ввиду, что сборщик мусора никогда не будет создавать граф для одного и того же объекта дважды, избегая необходимости выполнения подсчёта циклических ссылок, который характерен для программирования в среде COM.
Поколения объектов
При попытке обнаружить недостижимый код объекты CLR-среды не проверяют буквально каждый находящийся в куче объект. Очевидно, что на это уходила бы масса времени, особенно в крупных проектах.
Для оптимизации процесса каждый объект в куче относится к определённому «поколению» Смысл в применении поколений выглядит довольно просто:
- Поколение 0. Идентифицируется новый только что размещённый объект, который ещё никогда не помечался как надлежащий удалению в процессе сборки мусора
- Поколение 1. Идентифицирует объект, который уже «пережил» один процесс сборки мусора (был помечен, как надлежащий удалению, но не был удалён из-за достаточного свободного места в куче).
- Поколение 2. Идентифицирует объект, который пережил более одного прогона сбора мусора
Поколения 0 и 1 называются эфемерными.
Сборщик мусора сначала анализирует все объекты, которые относятся к поколению 0. Если после их удаления остаётся достаточное количество памяти, статус всех уцелевших объектов повышается до поколения 1. Если все объекты поколения 0 были проверены, но всё равно требуется дополнительное пространство, то будет запцщени проверка объектов поколения 1. Объекты этого поколения, которым удалось уцелеть, станут объектами поколения 2. если же сборщику мусора всё равно понадобится память, что сборке мусора подвергнуться объекты поколения 2. Так как объектов выше 2 поколения не бывает, то статус объектов не изменится.
Из всего вышесказанного можно сделать вывод, что более новые объекты будут удалятся быстрее, нежели более старые.
Заключение
КДПВ
Сборка мусора на основе подсчета ссылок
Сборщик мусора, опирающийся на подсчет ссылок, связывает каждый объект с целочисленной переменной - счетчиком ссылок. В момент создания объекта счетчик ссылок инициализируется значением 1. Когда приложение создает новую ссылку на объект, его счетчик ссылок увеличивается на 1. Когда приложение удаляет ссылку на существующий объект, его счетчик ссылок уменьшается на 1. Когда счетчик ссылок достигает значения 0, объект можно уничтожить и освободить занимаемую им память.
Одним из примеров управления памятью на основе подсчета ссылок в экосистеме Windows является объектная модель программных компонентов (Component Object Model, COM). Объекты COM снабжаются счетчиками ссылок, определяющими продолжительность их существования. Когда значение счетчика ссылок достигает 0, объект может освободить занимаемую им память. Основное бремя подсчета ссылок ложится на плечи разработчика, в виде явного вызова методов AddRef() и Release(), хотя в большинстве языков имеются обертки, автоматизирующие вызовы этих методов при создании и удалении ссылок.
Ниже перечислены основные проблемы, связанные с управлением памятью на основе подсчета ссылок:
Высокая стоимость управления: всякий раз, когда создается или уничтожается ссылка на объект, необходимо обновлять счетчик ссылок. Это означает, что к стоимости обновления ссылки прибавляются накладные расходы на выполнение таких тривиальных операций, как присваивание ссылки (a = b) или передача ссылки в функцию по значению. В многопроцессорных системах выполнение таких операций требует применения механизмов синхронизации и вызывает «пробуксовку» кеша процессора, при попытке нескольких потоков выполнения одновременно изменить счетчик ссылок.
Использование памяти: счетчик ссылок на объект должен храниться в памяти объекта. Это на несколько байтов увеличивает объем памяти, занимаемой объектом, что делает подсчет ссылок нецелесообразным для легковесных объектов. (Впрочем, это не такая большая проблема для CLR, где к каждому объекту «в нагрузку» добавляется от 8 до 16 байт.)
Правильность: при управлении памятью на основе подсчета ссылок возникает проблема утилизации объектов с циклическими ссылками. Если приложение больше не ссылается на некоторую пару объектов, но каждый из них продолжает хранить ссылку на другой объект (как показано на рисунке ниже), возникает утечка памяти. Эта проблема описывается в документации COM, где явно оговаривается, что такие циклические ссылки должны уничтожаться вручную. Другие платформы, такие как язык программирования Python, поддерживают дополнительные механизмы определения циклических ссылок и их устранения, применение которых влечет за собой увеличение стоимости сборки мусора.
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Управляет системным сборщиком мусора — службой, которая автоматически высвобождает неиспользуемую память.
Основы работы с памятью
В следующем списке перечислены важные понятия памяти среды CLR.
Каждый процесс имеет свое собственное отдельное виртуальное адресное пространство. Все процессы на одном компьютере совместно используют одну и ту же физическую память и один файл подкачки, если он есть.
По умолчанию на 32-разрядных компьютерах каждому процессу выделяется 2 Гбайт виртуального адресного пространства в пользовательском режиме.
Разработчики приложений работают только с виртуальным адресным пространством и никогда не управляют физической памятью напрямую. Сборщик мусора выделяет и освобождает виртуальную память для разработчика в управляемой куче.
При написании машинного кода для работы с виртуальным адресным пространством используются функции Windows. Эти функции выделяют и освобождают виртуальную память для разработчика в собственных кучах.
Виртуальная память может находиться в трех состояниях.
Виртуальное адресное пространство может стать фрагментированным. Это означает, что в адресном пространстве находятся свободные блоки, также известные как пропуски. Когда производится запрос на выделение виртуальной памяти, диспетчер виртуальной памяти должен найти один свободный блок достаточного размера для выполнения этого запроса на выделение. Даже если в системе есть 2 ГБ свободного пространства, операция выделения 2 ГБ завершится неудачей, если это пространство не расположено в одном адресном блоке.
Память может закончиться, если будет недостаточно виртуального адресного пространства для резервирования или физического пространства для выделения.
Файл подкачки используется, даже если нехватка физической памяти (то есть потребность в физической памяти) невелика. При первом возникновении нехватки физической памяти операционная система должна освободить пространство в физической памяти для хранения данных, для чего она производит резервное копирование некоторых данных, находящихся в физической памяти, в файл подкачки. Эти данные не выгружаются, пока в этом нет необходимости, так что с подкачкой можно столкнуться в ситуациях с небольшой нехваткой физической памяти.
Свойства
Возвращает наибольшее число поколений, поддерживаемое системой в настоящее время.
Выделение памяти
При инициализации нового процесса среда выполнения резервирует для него непрерывную область адресного пространства. Это зарезервированное адресное пространство называется управляемой кучей. Эта управляемая куча содержит указатель адреса, с которого будет выделена память для следующего объекта в куче. Изначально этот указатель устанавливается в базовый адрес управляемой кучи. Все ссылочные типы размещаются в управляемой куче. Когда приложение создает первый ссылочный тип, память для него выделяется, начиная с базового адреса управляемой кучи. При создании приложением следующего объекта сборщик мусора выделяет для него память в адресном пространстве, непосредственно следующем за первым объектом. Пока имеется доступное адресное пространство, сборщик мусора продолжает выделять пространство для новых объектов по этой схеме.
Выделение памяти из управляемой кучи происходит быстрее, чем неуправляемое выделение памяти. Так как среда выполнения выделяет память для объекта путем добавления значения к указателю, это осуществляется почти так же быстро, как выделение памяти из стека. Кроме того, поскольку выделяемые последовательно новые объекты располагаются в управляемой куче непрерывно, приложение может быстро получать доступ к ним.
Преимущества
Использование сборщика мусора обеспечивает следующие преимущества:
Разработчикам не нужно освобождать память вручную.
Эффективно выделяет память для объектов в управляемой куче.
Уничтожает объекты, которые больше не используются, очищает их память и сохраняет память доступной для будущих распределений. Управляемые объекты автоматически получают чистое содержимое, поэтому конструкторам не нужно инициализировать каждое поле данных.
Обеспечивает безопасность памяти, убедившись, что объект не может использовать для себя память, выделенную для другого объекта.
Читайте также: