Почему в Wasm нет сборщика мусора

Понимаем, почему базовый Wasm обходится без сборщика мусора.

Без GC — значит, что базовый Wasm не освобождает память автоматически: за линейную память отвечает скомпилированная программа.

Что это значит на практике

В JavaScript, Python или Java вы создаёте объекты, а сборщик мусора (garbage collector) сам находит ненужные и освобождает память. У базового WebAssembly такого механизма нет. Линейная память — просто массив байт; модуль сам решает, какой участок под что используется, когда «занять» и когда «освободить». Это ближе к миру C, где есть malloc и free.

Кто же управляет памятью

Ответ: та программа, которую скомпилировали в Wasm. Если вы пишете на C и компилируете в Wasm, ваш malloc/free превращаются в работу с линейной памятью. Если на Rust — компилятор сам расставляет освобождение по правилам владения. Логику управления памятью приносит исходный язык; Wasm лишь даёт сырой массив байт.

Простой аллокатор «в лоб»

Чтобы понять идею, представим простейший «бамп-аллокатор»: храним указатель на свободную позицию и просто двигаем его вперёд при каждом выделении. Смоделируем на JS:

let heapTop = 0;                  // граница занятой памяти
function malloc(bytes) {
  const addr = heapTop;          // адрес выдаём текущий
  heapTop += bytes;              // двигаем границу вперёд
  return addr;
}
console.log("блок 1 по адресу:", malloc(8));
console.log("блок 2 по адресу:", malloc(16));
console.log("блок 3 по адресу:", malloc(4));

Вывод:

блок 1 по адресу: 0
блок 2 по адресу: 8
блок 3 по адресу: 24

Это самый наивный аллокатор: он не умеет освобождать, только выдаёт новые куски. Настоящие аллокаторы (из стандартной библиотеки C или Rust) гораздо умнее, но идея та же — управление вручную, без GC за спиной.

Почему так сделано

Отсутствие GC по умолчанию — это плюс для целевой аудитории Wasm. Во-первых, предсказуемость: нет внезапных пауз, когда сборщик останавливает мир для уборки — критично для игр и реального времени. Во-вторых, универсальность: языки вроде C и Rust и так не имеют GC, и им незачем тащить чужой сборщик. В-третьих, малый размер: рантайм без GC компактнее.

А как же языки с GC?

Языки вроде Go, C# или Java приносят свой собственный сборщик мусора, скомпилированный в Wasm вместе с программой. Это работает, но увеличивает размер модуля. Поэтому в стандарт сейчас добавляют отдельное предложение WasmGC — встроенный в движок сборщик, чтобы такие языки не таскали свой и давали компактные модули. Об этом — в разделе про будущее.

Как работает под капотом

Когда вы вызываете malloc в скомпилированном из C коде, это не магия движка, а обычная функция вашей программы: она ведёт учёт свободных блоков прямо в линейной памяти и возвращает смещение. Движок Wasm об этом ничего не знает — для него это просто арифметика и обращения к памяти. Вся «бухгалтерия» памяти живёт внутри вашего же кода.

Частые ошибки

  • Ждать, что Wasm «сам уберёт» — нет, в базовом Wasm управление ручное (через рантайм языка).
  • Утечки памяти — если язык без GC и вы забыли free, память течёт, как в C.
  • Путать WasmGC и базовый Wasm — встроенный GC это отдельное расширение, а не свойство ядра.

Итоги

  • Базовый Wasm не имеет встроенного GC — память управляется вручную.
  • Логику malloc/free приносит исходный язык (C, Rust).
  • Отсутствие GC даёт предсказуемость, универсальность и компактность.
  • Языки с GC тащат свой сборщик; для них готовят расширение WasmGC.
Проверьте себя
1. Кто управляет линейной памятью в базовом Wasm?
AВстроенный в движок сборщик мусора
BСкомпилированная программа (рантайм исходного языка: C, Rust)
CОперационная система напрямую
DБраузерный JS автоматически
2. Почему отсутствие GC — преимущество для игр?
AПамять не нужна вовсе
BНет внезапных пауз на сборку мусора — предсказуемая производительность
CКод становится короче автоматически
DGC замедляет компиляцию
3. Что такое WasmGC?
AЧасть базового стандарта Wasm с самого начала
BОтдельное предложение — встроенный в движок сборщик, чтобы языки с GC не тащили свой
CУтилита для очистки .wasm файлов
DСборщик мусора для JavaScript