40-миллисекундная пауза Go GC из-за swap
A 40ms Go garbage collector pause caused by swap

Автор запустил swap в продакшене, чтобы сгладить пики памяти, и обнаружил неожиданный эффект: метаданные сборщика мусора Go могут оказаться в swap, что приводит к stop-the-world паузам до 40 мс. В тестах на Hetzner с ядром 6.8 и MGLRU медианная пауза составляла 51 мкс, но худший случай достиг 40 мс, из которых 39 мс ушли на 228 page faults в служебных структурах GC. Автор также заметил, что сборка сообщения размером 511 КиБ замедлилась с 3–5 мс до 105 мс на NVMe и до 903 мс на сетевом томе. Обновление: Green Tea GC в Go 1.26 не меняет ситуацию.
«40 мс — это в 800 раз больше медианной паузы. За время теста это происходит два-три раза на каждый всплеск памяти. Это много.»
- nasretdinov
К сожалению, это в какой-то степени ожидаемо — независимо от реализации, сам факт того, что GC должен так или иначе обойти всё дерево, чтобы отследить всё ещё используемые участки, делает swap крайне непрактичным — даже если бы у вас не было 40-миллисекундных stop-the-world пауз, LRU-кэш, используемый swap, выбрасывался бы при каждой сборке мусора.
Я полагаю, что именно по этой же причине Apple отказалась от GC в своих фреймворках в пользу автоматического подсчёта ссылок.
- jacobgold
«Мне больно, когда я делаю так»
«Перестань так делать»
Если вам важна задержка, отключите swap. Системно или для конкретной cgroup.
- pizlonator
Есть ли причина, по которой Go не использует on-the-fly GC, где STW вообще отсутствует?
- xavdid
Discord узнал об этом ещё в 2020 году и опубликовал отличный пост в блоге: https://discord.com/blog/why-discord-is-switching-from-go-to...
- soltanov
SLO по задержке GC должен учитывать давление на память операционной системы. Иначе проблема с page-fault будет выглядеть как проблема сборщика и приведёт к неверному исправлению.
- Gabrys1
Я начал задумываться, может ли существовать swap-aware GC, например, сначала подгрузить нужную страницу из swap (хотя очевидного API для этого нет...), и только потом останавливать мир?
- imclaren
Вот именно. Баги с раздуванием памяти в Go исправлять относительно легко, но иногда удаление раздувания из-за swap превращается в целое приключение. И я считаю, что стоит избавиться от любого раздувания из-за swap!
- faangguyindia
Я использую Go там, где использовал бы Node, Ruby или Python.
Я использую Rust там, где мне нужна низкая задержка.