Скільки використання стека занадто багато?


22

Останнім часом, коли я писав C або C ++, я оголошу всі свої змінні на стеці лише тому, що це варіант, на відміну від Java.

Однак я чув, що погана ідея оголошувати великі речі на стеці.

  1. Чому саме це так? Я думаю, що переповнення стека пов'язане, але мені не дуже зрозуміло, чому це відбувається.
  2. Скільки речей на стеку занадто багато?

Я не намагаюся розміщувати 100 МБ файлів на стеці, лише десяток кілобайтних масивів, щоб використовувати їх як буфери рядків або що завгодно. Це занадто багато використання стека?

(Вибачте, якщо дублікат, пошук стека продовжував давати посилання на переповнення стека. Немає навіть тегу стека викликів, я просто використав абстрактний.)


1
Як ви "ставите 100 МБ файлів у стек"? Реалізації буфера та контейнерів (і подібні на зразок std :: string) зазвичай використовують купу для зберігання їх корисного навантаження.
Мерфі

2
Ви можете піти з достатньою кількістю використання стеку за функцією / методом, поки не буде включена рекурсія, тоді ви ризикуєте сильно обмежити свої можливості, по відношенню до рекурсивної глибини, тому в рамках рекурсивних функцій потрібно використовувати як мінімум місцевих можливий змінний / стековий простір.
Ерік Ейдт

3
Зауважте, що C & C ++ відрізняються. Локальна std::vector<int>змінна не з'їсть багато місця у стеці, більшість даних знаходиться в купі.
Василь Старинкевич

Відповіді:


18

Це залежить від вашої операційної системи. Для Windows типовий максимальний розмір стека становить 1 Мб, тоді як він становить 8 МБ в типовому сучасному Linux, хоча ці значення можна регулювати різними способами. Якщо сума змінних ваших стеків (включаючи накладні накладні витрати, такі як зворотні адреси, аргументи на основі стека, заповнення значень повернення та байти вирівнювання) у всьому стеку викликів перевищує цю межу, ви отримуєте переповнення стека, яке, як правило, зменшує ваш програма без жодних шансів на відновлення.

Кілька кілобайт зазвичай штрафують. Десятки кілобайт небезпечно тим, що воно починає підбивати підсумки. Сотні кілобайт - дуже погана ідея.


1
Чи не обмеження типового стека на кілька мегабайт (тобто зазвичай більше одного, але, мабуть, менше десятка) сьогодні у 2016 році? На моєму робочому столі Linux за замовчуванням це 8 Мбайт ...
Базиль Старинкевич,

"У [...] Linux типовий максимальний розмір для стека становить 1 Мб", $ ulimit -aв моїй системі повертається серед інших stack size (kbytes, -s) 8192.
Мерфі

9

Єдина достовірна відповідь невизначена: "занадто багато, коли стек переповнюється".

Якщо ви не повністю контролюєте впровадження кожного рядка коду між точкою входу програми та відповідною функцією, ви не можете робити жодних припущень щодо кількості стеку. Наприклад, ви не можете гарантувати, що виклик цієї функції ніколи не спричинить переповнення стека:

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

Типовий стек у 8 MiB на сучасних Unixes - це досить багато місця, коли стеки йдуть, особливо тому, хто, як я, кому достатньо моторошника, щоб запам'ятати процесори з 8-бітовими покажчиками стеків. Практична реальність полягає в тому, що навряд чи ви проб'єте це, не намагаючись. Якщо це зробити, перевищення межі стека зазвичай вважається порушенням сегментації, і системи з достатньою кількістю керування пам'яттю для його виявлення надсилатимуть, SIGSEGVколи це станеться.

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


4

Якщо ви виділите масив, наприклад, 10000 байт на стеку, то цей масив обмежений за розміром. 10 000 може бути багато, але якщо вам потрібно 10 001 байт, то ваша програма може вийти з ладу або ще гірше. Тож у цій ситуації вам потрібно щось, що адаптується до потрібного вам розміру, і щось не буде в стеці.

Масиви фіксованого розміру для буферних рядків на стеку не є проблемою, оскільки вони зберігають пам'ять на стеці, вони є проблемою, оскільки буфери фіксованого розміру - це фатальна проблема, яка чекає, що трапиться.

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


2

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

Важливим є не виділяти великі шматки сирої пам’яті на стек, як char [1024 * 1024], і створювати класи для обгортання купових виділень і використовувати стек лише для зручності автоматичного виклику деструктора.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.