Коротко о самом главном в сборке мусора.

⚠️ Важно: это только концептуальная иллюстрация. В реальных средах исполнения JVM, .NET, Go и т.д. все сложнее!

? Терминология:

? Stop The World

В управляемых языках это "механизм" остановки ваших потоков, для того, чтобы GC мог почистить память от ненужных объектов.

? GC_POLL()

Фунция (на самом деле кусочек ассемблера), которая встраивается в ваш код и останавливает его исполнение по требованию GC.

? Safe points

Места в вашем коде, куда может встроиться функция GC_POLL()

Где Safe points и где может встроиться GC_POLL():

  1. На входе в функцию

  2. В конце горячих циклов (много итераций)

  3. На выходе из функции

  4. После вашего new (аллокации памяти) - если тип ссылочный

  5. После вызовов функций

  6. На определённых точках в коде с долгими вычислениями

  7. По желанию производителей компилятора...

⚠️ Safe points расставляются компилятором, программист напрямую на это не влияет.

⚙️ Примерный порядок действий(реализация зависит от конкретной среды C#, Java, Kotlin, Scala, Swift, Go и прочие):

  1. Компилятор вставляет в Вашу программу функцию и флаг, они доступны из любой точки программы

//Флаг остановки мира
volatile bool gc_stop_the_world = false;

inline void GC_POLL() {
    if (gc_stop_the_world) {
        //Функция прерывающая выполнение 
        //текущего потока кода, ваш код замирает
        SuspendCurrentThread(); 
    }
}
  1. Расставляет функцию 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() - представлен для урощения.

  1. При старте вашей программы запускается поток Вашей программы и отдельный поток/и 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 решает что памяти мало, он:

  1. Поднимает флаг gc_stop_the_world.

  2. Потоки самостоятельно доходят до GC_POLL() и приостанавливаются.

  3. GC ждет остановку всех нужных потоков

  4. Чистит память

  5. Cбрасывает флаг gc_stop_the_world.

  6. Возобновляет работу Ваших потоков.

⚠️ Ваш код сам может вызвать работу GC, если вы вызвали аллокацию (создали новый управляемый объект), а свободной памяти у менеджера памяти нет:

    var x = new byte[1000]; 
    // Этот код может сам заставить GC выполнить работу, 
    // потому-что на самом деле вызывается код аллокатора памяти 
    // и просит минимум 1000 байт

⚠️ Некоторые потоки могут быть неуправляемыми или сообщеить GC, что в данный момент их останавливать не нужно, тогда GC пропускает их при проверке.

Комментарии (0)


  1. EasyGame
    24.09.2025 08:38

    Смешались в кучу кони, люди... (c)


    1. MiniBytes Автор
      24.09.2025 08:38

      Да, я уже понял, что меня часто уносит в сторону)) По сути нужно было объяснить только StopTheWorld, первый блин комом... Буду учиться не миксовать такие смеси...


    1. MiniBytes Автор
      24.09.2025 08:38

      Поправил, оставил мясо....