Большинство GC - это так называемые GC "Stop the World" - в некоторых предопределенных точках ("Gc points" - например, точки вызова, прыжки, возвраты) каждый поток проверяет, существует ли какой-либо другой поток, который хочет сделать цикл GC. Как только все потоки остановлены, цикл GC будет запущен.
Конечно, существуют и другие возможности - GC в режиме реального времени, инкрементные, параллельные (и многое другое) - поиск в Интернете (вы в основном найдете опубликованные статьи) или просто покупка книги о сборщиках
Что касается сканирования стека, есть несколько способов:
точное сканирование:
стек с тегами - вы обычно держите 2 стека - один со значениями, а другой с «тегами». Что это за теги, зависит от того, что вам нужно, но в основном это будет отметка «это ссылка» / «не ссылка»
стеки без тегов - в основном, если у вас есть язык со строгими типами, для которого вы можете в любой момент времени (но чаще в каждой «точке GC») знать, какие типы находятся в стеке. Я приведу пример, используемый с простым интерпретатором (я только что придумал):
no-return function XY (int):
load_param 1
ipush 1
iadd
call Z (assume: int function Z (int, int))
new some_object
call Y
Если мы определим наши точки GC как call / new, то, возможно, есть 3 точки, в которых нам может понадобиться узнать типы наших стеков (при записи функции XY стек считается «пустым»):
- вызов Z - load_param загружает параметр типа int, ipush помещает в стек целые значения типа int - 2, для нашего GC здесь ничего не нужно делать
- new - вызов Z использует 2 целых числа из стека и помещает int
- call Y - новая размещенная ссылка, так что теперь у нас есть int и ссылка, GC должен знать об этой ссылке
Обратите внимание, что я сказал, что стек является "пустым" при входе в функцию - ну, на самом деле это не так, но вы можете проанализировать каждую функцию в отдельности, а затем просто переместиться вверх по "стеку вызовов" (у вас где-то есть указатель возврата, поэтому вы знать, куда нужно вернуться - return-1 - это вызов, для которого вы также можете получить образ стека. Повторяйте, пока не достигнете вершины).
Есть два способа запомнить эту информацию (для не тегированного стека):
- создать таблицу для каждой точки GC
- сгенерировать фрагмент кода для каждой точки GC (сам фрагмент кода будет отмечать ссылочные объекты)
Что касается того, когда вы собираете эту информацию, она может быть предварительно скомпилирована или как раз вовремя.
Это также может применяться к стекам машин, но все становится немного сложнее, поскольку вам, возможно, придется отслеживать и регистры.
Вы также сможете найти в Интернете несколько хороших статей о стеках без тегов.
Есть дополнительные модификации, где вы добавляете, например. информация «живучести» к вашим данным (хорошо, в стеке есть ссылка, но если в потоке инструкций, использующем его, нет кода, я могу теперь считать его недоступным)
- консервативный сбор (в отличие от точного сканирования) - где вы спрашиваете себя «это значение в стеке, интерпретируется как указатель, ссылка», если оно есть, то оно «живое». Это может, конечно, течь, если есть, например. целое число, которое выглядит как указатель (память будет освобождена, если целое число когда-либо изменится, но также может быть усеяно навсегда). Большинство сборщиков c / c ++ реализованы таким образом, поэтому ищите в этом направлении, если вам интересно.
Каждый ли процесс требует своего
рутина сбора мусора?
Нет ничего, что требовало бы этого, но я бы сказал, что это обычное дело - для простоты у меня был бы другой экземпляр GC (но только один для всех потоков) для каждого из моих процессов. Я полагаю, что между процессами может быть распределитель общей памяти - единственное преимущество, которое я вижу, - это, возможно, возможность лучше управлять фрагментацией памяти (потому что вы контролируете больше памяти), но сложность (взаимодействие / синхронизация inteproces - хм), объем общих данных и отсутствие независимости становятся проблемами. Я предполагаю здесь, и у меня нет никакого реального опыта с этими видами GC (или даже если они существуют) - это просто кажется мне здравым смыслом.
Это
хорошая идея запустить сборщик мусора
в отдельном потоке / процессе от
актуальная программа?
Ну, это зависит. Хорошей идеей было бы сохранить его в другом потоке, но от этого зависит то, хотите ли вы, чтобы ваш GC был простым («остановите мир» - все остальные потоки приостановлены, так что это практически не имеет значения в в каком потоке мы выполняем наш сборщик мусора, но он лучше, если у него есть собственный поток, или у вас есть особые требования (например, ваши потоки выполняются в режиме реального времени и не должны останавливаться на долгое время, тогда у вас будет другой сборщик мусора) использовать алгоритм реального времени / инкрементальный сборщик мусора).
Все зависит от того, что вам нужно, , но что бы вы ни делали, помните - делайте это как можно проще.
И я чуть не забыл, есть несколько отличных альтернатив написанию собственного GC с нуля - например. см. LLVM , они утверждают, что «используя эти инструменты, можно просто генерировать карты стека с точностью до типа для вашей среды выполнения всего за ~ 100 строк кода C ++». (только предварительно скомпилированный код, без поддержки генерации кода JIT, как для jet).
Кроме того, вы можете взглянуть на код для некоторых виртуальных машин Java (например, PhoneME Advanced VM (CVM), и, насколько я помню, kaffe довольно читабелен).
Отказ от ответственности: однажды я реализовал (в качестве студенческого проекта) точный, остановил мир, пометил, пометил и развернул GC - все, что здесь написано, является результатом того, что я помню из опыта, и должно быть правильным, но это может не будь "лучшей практикой". Исправления приветствуются.