Коротко о самом главном в сборке мусора.
⚠️ Важно: это только концептуальная иллюстрация. В реальных средах исполнения JVM, .NET, Go и т.д. все сложнее!
? Терминология:
? Stop The World
В управляемых языках это "механизм" остановки ваших потоков, для того, чтобы GC мог почистить память от ненужных объектов.
? GC_POLL()
Фунция (на самом деле кусочек ассемблера), которая встраивается в ваш код и останавливает его исполнение по требованию GC.
? Safe points
Места в вашем коде, куда может встроиться функция GC_POLL()
Где Safe points и где может встроиться GC_POLL():
На входе в функцию
В конце горячих циклов (много итераций)
На выходе из функции
После вашего new (аллокации памяти) - если тип ссылочный
После вызовов функций
На определённых точках в коде с долгими вычислениями
По желанию производителей компилятора...
⚠️ Safe points расставляются компилятором, программист напрямую на это не влияет.
⚙️ Примерный порядок действий(реализация зависит от конкретной среды C#, Java, Kotlin, Scala, Swift, Go и прочие):
Компилятор вставляет в Вашу программу функцию и флаг, они доступны из любой точки программы
//Флаг остановки мира
volatile bool gc_stop_the_world = false;
inline void GC_POLL() {
if (gc_stop_the_world) {
//Функция прерывающая выполнение
//текущего потока кода, ваш код замирает
SuspendCurrentThread();
}
}
Расставляет функцию GC_POLL() по вашему коду в безопасных точках (Safe Points)
Ваша функция:
void Foo(int count)
{
GC_POLL(); //Вы этого не писали
//...Какой-то ваш код
for(var i = 0; i < count; ++i)
{
//...Какой-то ваш код
GC_POLL() //Вы этого не писали
}
GC_POLL(); //Вы этого не писали
}
⚠️ Важно: компилятор вставляет минимальный ассемблерный код, а не прям call функции GC_POLL(), вызов GC_POLL() - представлен для урощения.
При старте вашей программы запускается поток Вашей программы и отдельный поток/и GC, например такая функция:
//Функция самого GC (Сборщик мусора, он же Garbage Collector)
//Она работает параллельно с вашей программой
void GC_WORK()
{
while(true)
{
//Проверяем надо ли чистить память
if(NeedToClearMemory())
{
//Выставляем флаг "остановки мира"
gc_stop_the_world = true;
//Ждем когда все нужные потоки остановятся
WaitStopTheWorld();
//Очистка памяти
ClearMemory();
//Сбрасываем флаг "остановки мира"
gc_stop_the_world = false;
//Запускаем потоки в работу
RunTheWorld();
}
else
{
Sleep(1); //Например можно засыпать на 1 мс
}
}
}
Когда GC решает что памяти мало, он:
Поднимает флаг gc_stop_the_world.
Потоки самостоятельно доходят до GC_POLL() и приостанавливаются.
GC ждет остановку всех нужных потоков
Чистит память
Cбрасывает флаг gc_stop_the_world.
Возобновляет работу Ваших потоков.
⚠️ Ваш код сам может вызвать работу GC, если вы вызвали аллокацию (создали новый управляемый объект), а свободной памяти у менеджера памяти нет:
var x = new byte[1000];
// Этот код может сам заставить GC выполнить работу,
// потому-что на самом деле вызывается код аллокатора памяти
// и просит минимум 1000 байт
⚠️ Некоторые потоки могут быть неуправляемыми или сообщеить GC, что в данный момент их останавливать не нужно, тогда GC пропускает их при проверке.
EasyGame
Смешались в кучу кони, люди... (c)
MiniBytes Автор
Да, я уже понял, что меня часто уносит в сторону)) По сути нужно было объяснить только StopTheWorld, первый блин комом... Буду учиться не миксовать такие смеси...
MiniBytes Автор
Поправил, оставил мясо....