Время от времени новички в Go натыкаются на любопытное свойство языка, связанное с размером стека, доступного горутине.
Обычно это происходит потому, что программист непреднамеренно создает бесконечную рекурсию.
Чтобы проиллюстрировать это, рассмотрим следующий (слегка надуманный) пример.
Если бы вы запустили эту программу, чего я не рекомендую вам делать, вы бы обнаружили, что ваша машина начала активно использовать swap и, вероятно, перестала бы отвечать на запросы, если бы вы не нажали ^C до того, как потеряли контроль.package main import "fmt" type S struct { a, b int } // String implements the fmt.Stringer interface func (s *S) String() string { return fmt.Sprintf("%s", s) // Sprintf will call s.String() } func main() { s := &S{a: 1, b: 2} fmt.Println(s) }
Так как первое, что все сделают, это попытаются запустить эту программу в песочнице, Я избавил тебя от неприятностей .
Большинство программистов раньше сталкивались с бесконечной рекурсией, и хотя она губительна для выполняемой программы, обычно она не влияет на всю машину.
Итак, чем же отличаются программы, написанные на Go? Одной из ключевых особенностей горутин является их стоимость; их создание дешево с точки зрения начального размера памяти (в отличие от 1–8 мегабайт для традиционного потока POSIX), а их стек увеличивается и сжимается по мере необходимости.
Это позволяет горутинам начинаться с одного стека размером 4096 байт, который затем увеличивается и сжимается по мере необходимости без риска когда-либо иссякнуть.
Для этого компоновщик (5l, 6l, 8l) вставляет в начало каждой функции небольшую «преамбулу» [1], которая проверяет, достаточно ли у этой функции доступной в данный момент памяти в стеке.
В случае, если функции требуется больше памяти, чем доступно на данный момент, вызывается среда выполнения morestack, которая выделяет новую страницу стека [2], копирует аргументы вызывающей стороны, а затем возвращает управление исходной функции, которая теперь может безопасно выполняться.
Когда эта функция завершает работу, происходит обратное: возвращенные аргументы копируются обратно во фрейм стека вызывающей стороны, и ненужное пространство стека освобождается.
Это делает стек в Go фактически бесконечным, и если вы не пересекаете постоянно границу между двумя стеками, это очень дешево.
Однако есть одна деталь, о которой я до сих пор не упомянул, которая связывает случайное использование рекурсивной функции с серьезной нехваткой памяти вашей операционной системы: когда Go требует новых страниц стека, они выделяются из кучи.
Поскольку ваша бесконечная функция продолжает вызывать сама себя, из кучи выделяются новые страницы стека, что позволяет функции вызывать себя снова и снова.
Довольно быстро размер кучи превысит объем свободной физической памяти на вашем компьютере, и в этот момент замена сделает вашу машину непригодной для использования.
Размер кучи, доступной программам Go, зависит от многих факторов, включая архитектуру вашего процессора и вашей операционной системы, но обычно он превышает физическую память вашей машины, поэтому машина, скорее всего, будет активно использовать подкачку до того, как программа исчерпает свою кучу.
В Go 1.1 было сильное желание увеличить максимальный размер кучи как для 32-битных, так и для 64-битных платформ, и это несколько усугубило проблему, т.к.
маловероятно, что у вас на вашем компьютере будет 128 ГБ [3] физической памяти.
система.
В заключение остается несколько открытых вопросов ( связь , связь ) относительно этой проблемы, но решение, которое бы не снижало производительность правильно написанных программ, пока не найдено.
Примечание переводчика: здесь Здесь Через шесть месяцев после написания оригинальной статьи (2013 год) авторы Go добавили ограничение памяти на размер стека горутины, это 1 ГБ для 64-битной и 250 МБ для 32-битной.
Примечания
- Это также относится к методам, но поскольку методы реализованы как функции, где первым аргументом является получатель метода, практической разницы между функциями и методами в этом контексте нет.
- Использование слова «страница» не означает, что возможно только фиксированное выделение 4096 байт, время выполнения⋅morestack при необходимости выделит большее количество, вероятно, округленное до границы страницы.
- 64-разрядные платформы Windows допускают использование кучи только 32 ГБ из-за позднего изменения в цикле выпуска Go 1.1.
-
Бонд, Уильям Кренч
19 Oct, 24 -
Программирование Под Дулом Пистолета
19 Oct, 24 -
Ретро-Монитор
19 Oct, 24 -
Учимся Писать: Быть Или Не Быть Собой
19 Oct, 24