Где в памяти живут глобальные переменные в Go?
Что проверяют
Понимаете ли вы, что кроме стека и кучи у программы есть статическая область данных. Ответ «в куче» принимается частично, «в стеке» неверен.
Как отвечать
Переменные уровня пакета живут всю жизнь программы, поэтому в стеке их быть не может: стек существует только на время вызова функции.
Компоновщик размещает их в статических сегментах исполняемого файла:
- сегмент данных для переменных с ненулевым начальным значением;
- сегмент bss для переменных, инициализированных нулём: в файле они места не занимают, память под них выделяется при запуске.
Если глобальная переменная это указатель, срез, карта или интерфейс, то сама переменная лежит в статической области, а данные, на которые она указывает, находятся в куче и участвуют в сборке мусора. Более того, глобальная переменная это корень для сборщика: всё, до чего можно добраться по ней, живо до конца работы программы.
Порядок инициализации тоже стоит назвать: сначала вычисляются значения переменных пакета в порядке зависимостей, затем выполняются функции init пакета, и только потом main.
Практические следствия: глобальная карта или срез, в которые складывают данные, это утечка по определению, потому что освободить их некому; глобальное изменяемое состояние требует синхронизации, поскольку доступ к нему идёт из всех горутин; из-за этого в Go принято передавать зависимости явно, а не хранить в переменных уровня пакета.
Где спотыкаются
Отвечают «в куче» и не могут объяснить, кто их освобождает (никто, они живут всегда).
Считают, что константы тоже где-то хранятся: константы подставляются в код на этапе компиляции.
Не знают про порядок инициализации и получают неожиданный ноль, когда одна глобальная переменная зависит от другой через функцию.