Как стать автором
Обновить

Комментарии 9

ох уж это просачивание абстракций - вместо того, чтобы чинить GC, или улучшать API :

Одно из наиболее интересных предложений заключалось в том, чтобы вообще удалить документацию и тем самым затруднить разработчикам использование sync.Pool.

... странно, что они там не предлагали сделать это же самое прямо на уровне языка (то есть удалить доки про Go <evil-grin.jpg>)

Во-первых, страницы стека горутин аллоцируются на хипе (по этой причине бесконечная рекурсия приводит в Go не к ошибке переполнения стека, а к ООМ). 

package test

import (
	"testing"
)

func recursive() {
	recursive()
}
func TestStackOverflow(t *testing.T) {
	recursive()
}
=== RUN   TestStackOverflow
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc020170390 stack=[0xc020170000, 0xc040170000]
fatal error: stack overflow

runtime stack:
runtime.throw({0x51f15a?, 0x5e1e80?})
	/home/xxx/sdk/go1.18.3/src/runtime/panic.go:992 +0x71
runtime.newstack()
	/home/xxx/sdk/go1.18.3/src/runtime/stack.go:1101 +0x5cc
runtime.morestack()
	/home/xxx/sdk/go1.18.3/src/runtime/asm_amd64.s:547 +0x8b

Что я делаю не так?

Попробуйте вызвать не как обычную рекурсивную функцию, а как горутину.

Спасибо за ваш вопрос! Вернемся к вам с обстоятельным ответом завтра. Нам требуется время на его подготовку.

Спасибо за интересный вопрос, постараюсь пояснить, что я имел в виду. Под "переполнением стека" я подразумевал классическую проблему stack overflow - ситуацию, при которой процесс полностью исчерпывает всю память в той области виртуального адресного пространства, которая отведена для хранения локальных переменных и адресов возврата. Актуальное значение размера стека можно узнать через параметр VmStk в /proc/$pid/status.

Так вот, при бесконечной рекурсии в Go VmStk вообще никак не изменяется, зато VmRSS, по которой можно косвенно оценить размер хипа, растёт практически линейно. Если ваш тест поместить в контейнер и ограничить ему потребление RSS до значения, меньшего чем 1000000000-byte limit (например, 256 Мб), то процесс завершится с обыкновенным OOM.

Поэтому Go просто эмулирует ошибку переполнения стека, чтобы защититься от ООМ.


Если же перенести ваш пример в С, то окажется, что там стекфреймы хранятся как раз на стеке. Если размер стека превышает дефолтный для Linux лимит в 8 Мб, то случается stack overflow, и программа падает с сегфолтом.

Исходники тестов и графиков выложил сюда.

Спасибо за титанический труд! Я начинающий разработчик, благодаря вашей статье лично я узнал много нового.

Виталий, спасибо за ваш опыт. Статью добавил в закладки. Попробую.

коллега! почему считаете, что Go отучает от внимательности?

Это субъективное ощущение. Я просто поймал себя на мысли о том, как тяжело мне переключаться на С/С++ после длительного периода работы на Go (а переключаться раз в несколько месяцев всё же приходится). Каждый раз обязательно наступаешь на грабли из стандартного набора (NPD, висячие указатели...). Но думаю, что это не проблема одного Go, это относится ко всем языкам с автоматическим управлением памятью.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий