Какие методы используют современные сборщики мусора (как в CLR, JVM), чтобы указать, какие объекты кучи ссылаются из стека?
В частности, как VM может вернуться от знания того, где стек начинает интерпретировать все локальные ссылки на объекты кучи?
Какие методы используют современные сборщики мусора (как в CLR, JVM), чтобы указать, какие объекты кучи ссылаются из стека?
В частности, как VM может вернуться от знания того, где стек начинает интерпретировать все локальные ссылки на объекты кучи?
В Java (и, скорее всего, в среде CLR, хотя я знаю, что ее внутренности хуже), байт-код вводится с объектной и примитивной информацией. В результате в байт-коде есть структуры данных, которые описывают, какие переменные в каждом стеке стека являются объектами и которые являются примитивами. Когда GC необходимо отсканировать корневой набор, он использует эти StackMapTables, чтобы различать ссылки и не ссылки.
CLR и Java должны иметь такой механизм, как это, потому что они являются точными сборщиками. Существуют консервативные коллекторы, такие как сборщик бэмов, которые обрабатывают каждое смещение в стеке как возможный указатель. Они видят, является ли значение (когда оно рассматривается как указатель) является смещением в кучу, и если да, то они отмечают его как живого.
Взгляните на эту статью Artima с августа 1996 года, кукурузную кучу Java Garbage; особенно на стр. 2.
Любой алгоритм сбора мусора должен делать две основные вещи. Во-первых, он должен обнаруживать мусорные объекты. Во-вторых, он должен вернуть кучу пространства, используемое мусорными объектами, и сделать его доступным для программы. Обнаружение мусора обычно достигается путем определения набора корней и определения достижимости от корней. Объект доступен, если есть некоторый путь ссылок от корней, с помощью которых исполняющая программа может обращаться к объекту. Корни всегда доступны для программы. Любые объекты, доступные из корней, считаются живыми. Объекты, которые недоступны, считаются мусором, поскольку они больше не могут влиять на будущий ход выполнения программы.
В JVM корневой набор зависит от реализации, но всегда будет включать любые ссылки на объекты в локальных переменных. В JVM все объекты находятся в куче. Локальные переменные находятся в стеке Java, и каждый поток выполнения имеет свой собственный стек. Каждая локальная переменная является либо ссылкой на объект, либо примитивным типом, таким как int, char или float. Поэтому корни любой собранной мусором JVM будут включать в себя каждую ссылку на объекты в каждом потоке стека. Другим источником корней являются любые ссылки на объекты, такие как строки, в постоянном пуле загруженных классов. Постоянный пул загруженного класса может ссылаться на строки, хранящиеся в куче, такие как имя класса, имя суперкласса, имена суперинтерфейса, имена полей, сигнатуры полей, имена методов и сигнатуры методов.
Любой объект, на который ссылается корень, доступен и, следовательно, является живым объектом. Кроме того, доступны объекты, на которые ссылается живой объект. Программа имеет доступ к любым доступным объектам, поэтому эти объекты должны оставаться в куче. Любые объекты, которые недоступны, могут быть собраны в мусор, поскольку для них нет доступа к ним.
В статье рассматриваются различные стратегии сбора мусора, в том числе сборщики подсчета ссылок, сборщики трассировки, сборщики уплотнений и копировальные коллекторы.
Хотя эта статья устарела, она по-прежнему применяется сегодня; мало что изменилось. Были усовершенствования производительности для различных стратегий сбора, но никаких новых крупных достижений.
Например, JVM Oracle HotSpot имеет новый Garbage-First Garbage Collector, который является копировальным коллектором с улучшенными настройками для многоядерных процессоров и больших размеров кучи (см. Этот ответ больше для G1 Garbage Collector).
Интересная документация по этой теме, опубликованная командой.Net вскоре после того, как они сделали исходный код CoreCLR: Stack Walking