Сколько стека используется слишком много?

22

В последнее время, когда я писал на C или C ++, я объявляю все свои переменные в стеке только потому, что это вариант, в отличие от Java.

Однако я слышал, что плохая идея объявлять большие вещи в стеке.

  1. Почему именно это так? Я полагаю, что переполнение стека связано, но я не очень понимаю, почему это происходит.
  2. Сколько вещей в стеке слишком много?

Я не пытаюсь поместить файлы размером 100 МБ в стек, просто дюжину килобайтных массивов для использования в качестве строковых буферов или чего-то еще. Это слишком большое использование стека?

(Извините, если дубликат, поиск стека продолжал давать ссылки на переполнение стека. Нет даже тега стека вызовов, я просто использовал абстрактный.)

Эллиот Уэй
источник
1
Как вы «помещаете файлы размером 100 МБ в стек»? Реализации буфера и контейнера (и аналогичные им как std :: string) обычно используют кучу для хранения своей полезной нагрузки.
Мерфи
2
Вы можете избежать использования достаточного количества стеков для каждой функции / метода, пока не будет задействована рекурсия, тогда вы рискуете серьезно ограничить свои возможности по сравнению с рекурсивной глубиной, поэтому в рекурсивных функциях вы хотите использовать как можно меньше локальных Переменная / пространство стека, насколько это возможно.
Эрик Эйдт
3
Обратите внимание, что C & C ++ отличаются. Локальная std::vector<int>переменная не будет занимать много места в стеке, большая часть данных находится в куче.
Василий Старынкевич

Ответы:

18

Это зависит от вашей операционной системы. В Windows типичный максимальный размер стека составляет 1 МБ, тогда как в типичном современном Linux он составляет 8 МБ, хотя эти значения можно регулировать различными способами. Если сумма переменных вашего стека (включая низкоуровневые издержки, такие как адреса возврата, основанные на стеке аргументы, заполнители возвращаемого значения и байты выравнивания) во всем стеке вызовов превышает этот предел, вы получаете переполнение стека, которое обычно снимает ваш Программа без каких-либо шансов на восстановление.

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

Себастьян Редл
источник
1
Разве типичный размер стека не ограничивается несколькими мегабайтами (то есть обычно больше одного, но, вероятно, меньше дюжины) сегодня в 2016 году? На моем рабочем столе Linux это по умолчанию 8 Мбайт ...
Старынкевич,
«В [...] Linux типичный максимальный размер стека составляет 1 МБ» $ ulimit -aв моей системе, среди прочих stack size (kbytes, -s) 8192.
Мерфи
9

Единственный правильный ответ - неопределенный: «слишком много, когда стек переполняется».

Если вы не полностью контролируете реализацию каждой строки кода между точкой входа программы и рассматриваемой функцией, вы не можете делать предположения о том, какой объем стека доступен. Например, вы не можете гарантировать, что вызов этой функции никогда не вызовет переполнение стека:

void break_the_camels_back()
{
    int straw;
    ...
}

Стек по умолчанию 8 Мбайт в современных Unix-системах достаточно просторен для стеков, особенно для таких, как я, которым достаточно чудака, чтобы помнить процессоры с 8-битными указателями стеков. Практическая реальность такова, что вы вряд ли прорвитесь через это, не пытаясь. Если вы это сделаете, превышение предела стека обычно считается нарушением сегментации, и системы с достаточным управлением памятью, чтобы обнаружить его, отправят сообщение, SIGSEGVкогда это произойдет.

У вас есть несколько вариантов. Первый - не угадать, сколько стека доступно, и спросить систему. Все, что соответствует POSIX, будет иметь getrlimit(2)функцию, которая сообщит вам верхний предел. RLIMIT_STACKэто конкретный предел, который вы хотите. Второй - отслеживать, сколько стека используют ваши программы, и принимать решения об автоматических переменных в зависимости от динамического распределения памяти. Насколько я знаю, нет стандартных функций для определения объема стека, но такие программы, как подобные, valgrindмогут проанализировать его для вас.

Blrfl
источник
4

Если вы выделите массив, скажем, 10 000 байтов в стеке, то этот массив будет ограничен в размере. 10 000 может быть много, но если вам нужно 10 001 байт, то ваша программа может вылетать или даже хуже. Таким образом, в этой ситуации вы хотите что-то, что адаптируется к нужному размеру, и что-то не будет в стеке.

Массивы фиксированного размера для строковых буферов в стеке не являются проблемой, потому что они сохраняют память в стеке, они являются проблемой, потому что буферы фиксированного размера являются фатальной проблемой, ожидающей своего появления.

Но если вы используете C ++ и объявляете, например, std :: string или std :: vec в стеке, то то, что находится в стеке, будет на самом деле фиксированного и небольшого размера. Фактические данные будут храниться в куче. Вы можете хранить миллион символов в экземпляре std :: string, и он будет занимать только очень небольшой объем данных (обычно от 8 до 24 байт, в зависимости от реализации) в стеке и миллион байтов в куче.

gnasher729
источник
2

Ну, 1 МБ - хорошая оценка для * nix. Рекурсия может быть основной причиной переполнения стека в сочетании с выделением стека. Однако в большинстве случаев объекты бога, которые внешне кажутся слишком большими для размещения в стеке, хорошо спроектированы для управления их внутренней памятью в куче и использования стека только в качестве способа автоматического уничтожения при извлечении стека. Деструктор освободит огромные куски памяти, управляемые изнутри. Контейнеры std спроектированы таким образом, и общие / уникальные указатели спроектированы таким же образом.

Важно не выделять большие куски необработанного mem в стеке, такие как char [1024 * 1024], и разрабатывать классы для переноса распределений в куче и использовать стек только для удобства автоматического вызова деструктора.

звездочка
источник