Почему Стек Горутины Бесконечен?

Время от времени новички в Go натыкаются на любопытное свойство языка, связанное с размером стека, доступного горутине.

Обычно это происходит потому, что программист непреднамеренно создает бесконечную рекурсию.

Чтобы проиллюстрировать это, рассмотрим следующий (слегка надуманный) пример.

   

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) }

Если бы вы запустили эту программу, чего я не рекомендую вам делать, вы бы обнаружили, что ваша машина начала активно использовать swap и, вероятно, перестала бы отвечать на запросы, если бы вы не нажали ^C до того, как потеряли контроль.

Так как первое, что все сделают, это попытаются запустить эту программу в песочнице, Я избавил тебя от неприятностей .

Большинство программистов раньше сталкивались с бесконечной рекурсией, и хотя она губительна для выполняемой программы, обычно она не влияет на всю машину.

Итак, чем же отличаются программы, написанные на 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-битной.



Примечания

  1. Это также относится к методам, но поскольку методы реализованы как функции, где первым аргументом является получатель метода, практической разницы между функциями и методами в этом контексте нет.
  2. Использование слова «страница» не означает, что возможно только фиксированное выделение 4096 байт, время выполнения⋅morestack при необходимости выделит большее количество, вероятно, округленное до границы страницы.

  3. 64-разрядные платформы Windows допускают использование кучи только 32 ГБ из-за позднего изменения в цикле выпуска Go 1.1.
Теги: #программирование #Go #golang #никто не читает теги
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.