Почему в 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.