Стек и куча не такие разные, как вы думаете!
Правда, некоторые операционные системы имеют ограничения по стеку. (Некоторые из них также имеют неприятные ограничения кучи!)
Но это уже не 1985 год.
В эти дни я использую Linux!
Мое значение по умолчанию Размер стека ограничено 10 МБ. Мое значение по умолчанию heapsize не ограничено. Это довольно тривиально, чтобы ограничить размер стека. (* кашель * [tcsh] неограниченный размер стека * кашель *. Или setrlimit () .)
Самые большие различия между стеком и кучей :
- стек выделения просто смещают указатель (и, возможно, выделяют новые страницы памяти, если стек стал достаточно большим). Heap должен искать в своих структурах данных, чтобы найти подходящий блок памяти. (И, возможно, выделять новые страницы памяти тоже.)
- стек выходит из области видимости, когда заканчивается текущий блок. Heap выходит из области действия при вызове delete / free.
- Куча может быть фрагментирована. Стек никогда не фрагментируется.
В Linux и стек и куча управляются через виртуальную память.
С точки зрения распределения времени, даже поиск в куче в плохо фрагментированной памяти не может удержать отображение на новых страницах памяти. По времени различия незначительны!
В зависимости от вашей ОС, часто только когда вы фактически используете те новые страницы памяти, в которые они отображаются. ( НЕ во время выделения malloc () !) (Это ленивая оценка вещь.)
( new вызовет конструктор, который предположительно будет использовать эти страницы памяти ...)
Вы можете уничтожить систему ВМ, создав и уничтожив крупные объекты либо в стеке , либо в куче . Это зависит от вашей ОС / компилятора, может ли память быть восстановлена системой. Если это не исправлено, куча могла бы повторно использовать это. (Предполагая, что он не был повторно перенаправлен другим malloc () за это время.) Аналогично, если стек не будет восстановлен, он будет просто повторно использован.
Хотя страницы, которые меняются местами, должны быть возвращены обратно, и это будет ваш самый большой удар по времени.
Из всех этих вещей Я больше всего беспокоюсь о фрагментации памяти !
Продолжительность жизни (когда она выходит за рамки) всегда является решающим фактором.
Но когда вы запускаете программы в течение длительных периодов времени, фрагментация создает постепенно увеличивающийся объем памяти. Постоянный обмен в конечном итоге убивает меня!
ИЗМЕНЕНО В ДОБАВЛЕНИИ:
Чувак, я был избалован!
Что-то здесь просто не складывалось ... Я тоже подумала, что * я * чертовски далеко от базы. Или все остальные были. Или, скорее, оба. Или, может быть, ни то, ни другое.
Каким бы ни был ответ, я должен был знать, что происходит!
... Это будет долго. Терпи меня ...
Я провел большую часть последних 12 лет, работая под Linux. И примерно за 10 лет до этого под разными вкусами Unix. Мой взгляд на компьютеры несколько предвзят. Я был избалован!
Я немного поработал с Windows, но недостаточно, чтобы говорить авторитетно. Как ни трагично, но и с Mac OS / Darwin ... Хотя Mac OS / Darwin / BSD достаточно близки, чтобы некоторые из моих знаний были перенесены.
При использовании 32-разрядных указателей вам не хватает адресного пространства в 4 ГБ (2 ^ 32).
С практической точки зрения, STACK + HEAP в сочетании обычно ограничен где-то между 2-4 ГБ, так как другие вещи должны отображаться там.
(есть общая память, общие библиотеки, отображенные в память файлы, исполняемый образ, который вы всегда используете, и т. Д.)
В Linux / Unix / MacOS / Darwin / BSD вы можете искусственно ограничить HEAP или STACK любыми произвольными значениями, которые вы хотите во время выполнения. Но в конечном итоге существует жесткое ограничение системы.
Это различие (в tcsh) "limit" vs "limit -h" . Или (в bash) "ulimit -Sa" vs "ulimit -Ha" . Или, программно, из rlim_cur против rlim_max в struct rlimit .
Теперь перейдем к самой интересной части. В отношении Код Мартина Йорка . (Спасибо Мартин ! Хороший пример. Всегда хорошо пробовать!)
Мартин предположительно работает на Mac. (Довольно недавний. Его компилятор новее моего!)
Конечно, его код не будет работать на его Mac по умолчанию. Но он будет работать нормально, если он сначала вызовет "unlimit stacksize" (tcsh) или "ulimit -Ss unlimited" (bash).
СЕРДЦЕ МАТЕРИИ:
Тестирование на древнем (устаревшем) блоке ядра Linux RH9 2.4.x с выделением большого количества STACK ИЛИ HEAP , либо один из них сам по себе завершается от 2 до 3 ГБ. (К сожалению, RAM + SWAP машины занимает чуть меньше 3,5 ГБ. Это 32-битная ОС. И это НЕ единственный процесс, который выполняется. Мы справляемся с тем, что имеем ...)
Так что на самом деле нет никаких ограничений на размер STACK против HEAP под Linux, кроме искусственных ...
НО:
На Mac жесткое ограничение размера стека составляет 65532 килобайт . Это связано с тем, как что-то заложено в память.
Обычно вы думаете об идеализированной системе как имеющей STACK на одном конце адресного пространства памяти, HEAP на другом, и они строятся навстречу друг другу. Когда они встречаются, вам не хватает памяти.
Mac, кажется, прикрепляют свои Общие системные библиотеки между ними с фиксированным смещением, ограничивающим обе стороны. Вы по-прежнему можете запускать код Мартина Йорка с "неограниченным размером стека", поскольку он выделяет только около 8 МБ (<64 МБ) данных. <strong>Но у него кончится STACK задолго до того, как у него кончится HEAP .
Я нахожусь на Linux. Я не буду. Прости, малыш. Вот никель. Возьми себе лучшую ОС.
Есть обходные пути для Mac. Но они становятся уродливыми и грязными и требуют настройки параметров ядра или компоновщика.
В конечном итоге, если Apple не сделает что-то действительно глупое, 64-битные адресные пространства сделают всю эту проблему с ограничением стека устаревшей когда-то в реальном времени.
Переход к фрагментации:
Каждый раз, когда вы помещаете что-то в STACK , оно добавляется в конец И он удаляется (откатывается) при выходе из текущего блока.
В результате в STACK нет отверстий. Это все один большой сплошной блок используемой памяти. Возможно, в самом конце немного неиспользуемого пространства, готового к повторному использованию.
Напротив, когда HEAP выделяется и освобождается, вы получаете дыры в неиспользуемой памяти. Это может постепенно привести к увеличению объема памяти с течением времени. Не то, что мы обычно подразумеваем под утечкой активной зоны, но результаты схожи.
Фрагментация памяти НЕ причина избегать хранения HEAP . Это просто то, что нужно знать, когда вы кодируете.
Что вызывает SWAP THRASHING :
- Если у вас уже есть большое количество выделенной / используемой кучи.
- Если у вас разбросано много фрагментированных отверстий.
- А если у вас большое количество небольших выделений.
Затем вы можете получить большое количество переменных, которые используются в небольшой локализованной области кода, которые разбросаны по множеству страниц виртуальной памяти. (Как вы используете 4 байта на этой странице 2k, и 8 байтов на этой странице 2k, и так далее для целого ряда страниц ...)
Все это означает, что для запуска вашей программы требуется большое количество страниц. Или это будет постоянно обмениваться страницами. (Мы называем это избиением.)
С другой стороны, если бы эти небольшие выделения были сделаны на STACK , все они были бы расположены в непрерывном отрезке памяти. Меньше страниц памяти VM должно быть загружено. (4 + 8 + ... <2k за победу.) </p>
Sidenote: Моя причина для привлечения внимания к этому исходит от некоего инженера-электрика, которого я знал, который настаивал на том, чтобы все массивы были размещены в HEAP. Мы делали математику для графики. * LOT * из 3 или 4 элементов массива. Управление только новым / удалением было кошмаром. Даже отвлекшись на занятиях, это вызвало горе!
Следующая тема. Заправка:
Да, потоки по умолчанию ограничены очень маленькими стеками.
Вы можете изменить это с помощью pthread_attr_setstacksize (). Хотя в зависимости от вашей реализации потоков, если несколько потоков совместно используют одно и то же 32-разрядное адресное пространство, большие отдельные стеки для каждого потока будут проблемой! Там просто не так много места! Опять же, переход на 64-битные адресные пространства (ОС) поможет.
pthread_t threadData;
pthread_attr_t threadAttributes;
pthread_attr_init( & threadAttributes );
ASSERT_IS( 0, pthread_attr_setdetachstate( & threadAttributes,
PTHREAD_CREATE_DETACHED ) );
ASSERT_IS( 0, pthread_attr_setstacksize ( & threadAttributes,
128 * 1024 * 1024 ) );
ASSERT_IS( 0, pthread_create ( & threadData,
& threadAttributes,
& runthread,
NULL ) );
Относительно Мартина Йорка Рамки стека:
Возможно, мы с тобой думаем о разных вещах?
Когда я думаю о кадре стека , я думаю о стеке вызовов. Каждая функция или метод имеет свой собственный стековый фрейм , состоящий из адреса возврата, аргументов и локальных данных.
Я никогда не видел никаких ограничений на размер стекового фрейма . Существуют ограничения на STACK в целом, но это все стековых фреймов вместе взятых.
На Wiki есть хорошая диаграмма и обсуждение стековых фреймов .
На последнем примечании:
В Linux / Unix / MacOS / Darwin / BSD можно программно изменить максимальные ограничения STACK , а также limit (tcsh) или ulimit (Баш):
struct rlimit limits;
limits.rlim_cur = RLIM_INFINITY;
limits.rlim_max = RLIM_INFINITY;
ASSERT_IS( 0, setrlimit( RLIMIT_STACK, & limits ) );
Только не пытайтесь установить его на INFINITY на Mac ... И измените его, прежде чем пытаться его использовать. ;-)
Дополнительное чтение: