25 Вопрос: Что и где находится стек и куча?

вопрос создан в Wed, Apr 10, 2019 12:00 AM

Книги по языку программирования объясняют, что типы значений создаются в стеке , а ссылочные типы создаются в куче , не объясняя, что это за две вещи. Я не прочитал четкое объяснение этого. Я понимаю, что такое стек . Но

  • Где и что они (физически в памяти реального компьютера)?
  • В какой степени они контролируются операционной системой или языком выполнения?
  • Какова их сфера применения?
  • От чего зависит размер каждого из них?
  • Что делает быстрее?
7663
  1. действительно хорошее объяснение можно найти здесь В чем разница между стеком и кучей?
    2013-12-16 11: 32: 49Z
  2. Также (действительно) хорошо: codeproject.com/Articles/76153/… (часть стека /кучи)
    2014-02-15 05: 50: 34Z
  3. 2016-06-11 05: 42: 54Z
  4. Связанные, см. Столкновение стэков . Исправления Stack Clash повлияли на некоторые аспекты системных переменных и поведения, например rlimit_stack. Также см. Red Hat выпуск 1463241
    2017-06-21 16: 23: 19Z
  5. @ mattshane Определения стека и кучи не зависят от значения и ссылочных типов. Другими словами, стек и куча могут быть полностью определены, даже если значения и ссылочные типы никогда не существовали. Кроме того, при понимании значений и ссылочных типов стек является лишь деталью реализации. Пер Эрик Липперт noreferrer "> Стек - это деталь реализации, часть первая .
    2017-11-12 22: 38: 03Z
25 ответов                              25                         

Стек - это память, выделенная как пустое место для потока выполнения. Когда вызывается функция, блок резервируется в верхней части стека для локальных переменных и некоторых бухгалтерских данных. Когда эта функция возвращается, блок становится неиспользованным и может быть использован при следующем вызове функции. Стек всегда зарезервирован в порядке LIFO (последний в порядке поступления); последний зарезервированный блок всегда является следующим блоком, который должен быть освобожден. Это действительно упрощает отслеживание стека; освобождение блока из стека - это не более, чем настройка одного указателя.

Куча - это память, выделенная для динамического выделения. В отличие от стека, нет принудительного шаблона распределения и удаления блоков из кучи; Вы можете выделить блок в любое время и освободить его в любое время. Это значительно усложняет отслеживание того, какие части кучи выделены или свободны в любой момент времени; Есть много пользовательских распределителей кучи, доступных для настройки производительности кучи для различных моделей использования.

Каждый поток получает стек, хотя обычно для приложения имеется только одна куча (хотя нередко иметь несколько куч для разных типов размещения).

Чтобы ответить на ваши вопросы напрямую:

  

В какой степени они контролируются операционной системой или языковой средой выполнения?

ОС выделяет стек для каждого системного уровня tHread, когда поток создан. Обычно ОС вызывается языковой средой выполнения для выделения кучи для приложения.

  

Какова их область применения?

Стек присоединен к потоку, поэтому, когда поток выходит из стека, он освобождается. Куча обычно выделяется при запуске приложения средой выполнения и восстанавливается при выходе из приложения (технически процесса).

  

От чего зависит размер каждого из них?

Размер стека устанавливается при создании потока. Размер кучи устанавливается при запуске приложения, но может увеличиваться по мере необходимости (распределитель запрашивает больше памяти у операционной системы).

  

Что делает его быстрее?

Стек работает быстрее, потому что шаблон доступа упрощает выделение и освобождение памяти из него (указатель /целое число просто увеличивается или уменьшается), в то время как куча имеет гораздо более сложную бухгалтерию, связанную с выделением или освобождением. Кроме того, каждый байт в стеке имеет тенденцию использоваться очень часто, что означает, что он, как правило, отображается в кэш процессора, что делает его очень быстрым. Другим ударом производительности для кучи является то, что куча, являющаяся главным образом глобальным ресурсом, как правило, должна быть многопоточной безопасной, то есть каждое выделение и освобождение должны быть - как правило - синхронизированы со «всеми» другими обращениями к куче в программе.

Четкая демонстрация:
Источник изображения: vikashazrati.wordpress.com

    
5652
2016-05-02 12: 35: 48Z
  1. Хороший ответ - но я думаю, вам следует добавить, что хотя стек выделяется ОС при запуске процесса (при условии существования ОС), он поддерживается встроенным по программе. Это еще одна причина, по которой стек работает быстрее: операции push и pop обычно представляют собой одну машинную инструкцию, и современные машины могут выполнять как минимум 3 из них за один цикл, тогда как выделение или освобождение кучи требует обращения к коду ОС.
    2013-10-08 08: 31: 45Z
  2. Я действительно смущен диаграммой в конце. Я думал, что получил его, пока не увидел это изображение.
    2016-08-15 19: 06: 04Z
  3. @ Anarelle процессор выполняет инструкции с или без OS. Пример, близкий моему сердцу, - это SNES, у которого не было ни вызовов API, ни ОС, как мы ее знаем сегодня, - но у нее был стек. Выделение в стеке - сложение и вычитание в этих системах, и это хорошо для переменных, уничтожаемых, когда они выталкиваются, возвращаясь из функции, которая их создала, но ограничивают это, скажем, конструктором, результат которого не может быть просто выброшенный. Для этого нам нужна куча, которая не привязана к вызову и возврату. У большинства ОС есть куча API, нет причин делать это самостоятельно
    2016-10-13 15: 06: 55Z
  4. "стек - это память, выделенная как пустое место". Здорово. Но где это на самом деле «откладывается» с точки зрения структуры памяти Java? Это куча памяти /не куча памяти /Другое (структура памяти Java согласно betsol.com/2017/06/… )
    2018-07-22 06: 22: 38Z
  5. @ JatinShashoo Среда исполнения Java, как интерпретатор байт-кода, добавляет еще один уровень виртуализации, так что вы упомянули лишь точку зрения приложения Java. С точки зрения операционной системы все это просто куча, где исполняющий процесс Java выделяет часть своего пространства как память «без кучи» для обработанного байт-кода. Остальная часть этой кучи на уровне ОС используется в качестве кучи на уровне приложения, где хранятся данные объекта.
    2018-09-06 15: 41: 48Z

Stack

  • Хранится в оперативной памяти компьютера, как в куче.
  • Переменные, созданные в стеке, выйдут из области видимости и будут автоматически освобождены.
  • Гораздо быстрее выделить по сравнению с переменными в куче.
  • Реализовано с фактической структурой данных стека.
  • Хранит локальные данные, адреса возврата, используемые для передачи параметров.
  • Может иметь переполнение стека, когда используется слишком большая часть стека (в основном из бесконечной или слишком глубокой рекурсии, очень больших выделений).
  • Данные, созданные в стеке, могут использоваться без указателей.
  • Вы бы использовали стек, если точно знаете, сколько данных нужно выделить перед компиляцией, и он не слишком большой.
  • Обычно максимальный размер уже определен при запуске вашей программы.

Heap:

  • Хранится в оперативной памяти компьютера, как и стек.
  • В C ++ переменные в куче должны быть уничтожены вручную и никогда не выходить за рамки. Данные освобождаются с delete, delete[] или free.
  • Медленнее размещать по сравнению с переменными в стеке.
  • Используется по требованию для выделения блока данных для использования программой.
  • Может иметь фрагментацию при большом количестве выделений и освобождений.
  • В C ++ или C данные, созданные в куче, будут указываться указателями и размещаться с new или malloc соответственно.
  • Может иметь ошибки выделения, если запрошен слишком большой буфер.
  • Вы бы использовали кучу, если не знаете точно, сколько данных вам потребуется во время выполнения или если вам нужно выделить много данных.
  • Ответственный за утечки памяти.

Пример: сильный> р>  

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
    
2237
2017-07-28 00: 38: 33Z
  1. Указатель pBuffer и значение b расположены в стеке и, скорее всего, размещаются на входе в функцию. В зависимости от компилятора, буфер также может быть выделен на входе функции.
    2009-03-18 22: 48: 52Z
  2. Распространенным заблуждением является то, что язык C, как определено стандартом языка C99 (доступен по адресу open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf ), требуется «стек». На самом деле, слово «стек» даже не встречается в стандарте. Это отвечает заявлениям относительно использования стека C в общем, верно, но никоим образом не требуется языком. См. knosof.co.uk/cbook/cbook.html для получения дополнительной информации, и, в частности, как C реализован в нечетных архитектурах, таких как en.wikipedia.org/wiki/Burroughs_large_systems
    2009-09-01 04: 37: 30Z
  3. @ Brian Вы должны объяснить почему buffer [] и указатель pBuffer создаются в стеке и почему данные pBuffer создаются в куче. Я думаю, что некоторые ответы могут быть смущены вашим ответом, поскольку они могут подумать, что программа специально указывает, что память должна быть выделена в стеке, а не в куче, но это не так. Это потому, что Buffer является типом значения, тогда как pBuffer является ссылочным типом?
    2010-02-08 04: 56: 47Z
  4. @ Remover: ни один указатель не содержит адрес, и он может указывать на что-то в куче или стеке одинаково. new, malloc и некоторые другие функции, аналогичные malloc, выделяют в куче и возвращают адрес памяти, которая была выделена. Почему вы хотите выделить в куче? Так что ваша память не выйдет из области видимости и будет освобождена, пока вы этого не захотите.
    2012-05-26 23: 25: 58Z
  5. "Ответственный за утечки памяти" - Кучи не несут ответственности за утечки памяти! Ленивые /Забывчивые /бывшие Java-кодеры /кодеры, которые не дают дерьма, являются!
    2013-03-25 08: 22: 06Z

Наиболее важным моментом является то, что куча и стек являются общими терминами.для способов, которыми может быть выделена память. Они могут быть реализованы разными способами, и эти термины применяются к основным понятиям.

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

    Стопка, как стопка бумаг

    Простота стека заключается в том, что вам не нужно поддерживать таблицу, содержащую запись каждого раздела выделенной памяти; единственная информация о состоянии, которая вам нужна, - это единственный указатель на конец стека. Чтобы выделить и удалить, вы просто увеличиваете и уменьшаете этот единственный указатель. Примечание: иногда может быть реализован стек, который начинается сверху раздела памяти и расширяется вниз, а не растёт вверх.

  • В куче нет определенного порядка расположения элементов. Вы можете получать и удалять предметы в любом порядке, потому что нет четкого «верхнего» предмета.

    Куча, как куча ассорти из солодки

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

Эти изображения должны довольно хорошо описать два способа выделения и освобождения памяти в стеке и куче. Yum!

  • В какой степени они контролируются операционной системой или языковой средой выполнения?

    Как уже упоминалось, куча и стек являются общими терминами и могут быть реализованы многими способами. Компьютерные программы обычно имеют стек, называемый стек вызовов , в котором хранится информация, относящаяся к текущей функции, например указатель на функцию, из которой она была вызвана, и любые локальные переменные. Поскольку функции вызывают другие функции и затем возвращаются, стек увеличивается и сжимается, чтобы хранить информацию от функций дальше по стеку вызовов. Программа на самом деле не имеет контроля над ней во время выполнения; это определяется языком программирования, ОС и даже архитектурой системы.

    Куча - это общий термин, используемый для любой памяти, которая выделяется динамически и случайным образом; то есть не в порядке. Память обычно выделяется ОС, при этом приложение вызывает функции API для этого. При управлении динамически выделяемой памятью требуется немало накладных расходов, которые обычно обрабатываются ОС.

  • Какова их сфера применения?

    Стек вызовов является настолько низкоуровневым понятием, что не имеет отношения к «объему» в смысле программирования. Если вы разберете некоторый код, вы увидите относительные ссылки на стиль указателя на части стека, но, что касается языка более высокого уровня, язык налагает свои собственные правила области видимости. Однако одним важным аспектом стека является то, что, как только функция возвращается, все, что является локальным для этой функции, немедленно освобождается из стека. Это работает так, как вы и ожидаете, учитывая работу ваших языков программирования. В куче это тоже сложно определить. Область действия - это то, что доступно операционной системе, но ваш язык программирования, вероятно, добавляет свои правила о том, что такое «область действия» в вашем приложении. Архитектура процессора и ОС используют виртуальную адресацию, которую процессор преобразует в физические адреса, имеются сбои страниц и т. Д. Они отслеживают, какие страницы принадлежат каким приложениям. Однако вам никогда не нужно беспокоиться об этом, потому что вы просто используете любой метод, используемый вашим языком программирования, для выделения и освобождения памяти, и проверяете на наличие ошибок (если по какой-либо причине распределение /освобождение не удается).

  • От чего зависит размер каждого из них?

    Опять же, это зависит от языка, компилятора, операционной системы и архитектуры. Стек обычно предварительно выделяется, потому что по определению он должен быть смежной памятью (подробнее об этом в последнем абзаце). Компилятор языка или ОС определяют его размер. Вы не храните огромные порции данных в стеке, поэтому они будут достаточно большими, чтобы никогда не использовать их полностью, за исключением случаев нежелательной бесконечной рекурсии (отсюда «переполнение стека») или других необычных программных решений.

    Куча - это общий термин для всего, что может быть динамически выделено. В зависимости от того, как вы на это смотрите, размер постоянно меняется. В современных процессорах и операционных системах точный способ работыв любом случае он очень абстрагирован, поэтому вам обычно не нужно сильно беспокоиться о том, как он работает в глубине, за исключением того, что (в языках, где это позволяет) вы не должны использовать память, которую вы еще не распределили, или память, которая у вас есть освобождена.

  • Что делает его быстрее?

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

1323
2017-12-17 22: 26: 52Z
  1. Дэвид. Я не согласен с тем, что это хорошее изображение или что «стек push-down» - хороший термин для иллюстрации концепции. Когда вы добавляете что-либо в стек, другое содержимое стека не сдвигается вниз, оно остается на своем месте.
    2012-08-13 03: 40: 14Z
  2. Этот ответ содержит большую ошибку. Статические переменные не размещаются в стеке. См. Мой ответ [ссылка] stackoverflow.com/a/13326916/1763801 для уточнения. вы приравниваете «автоматические» переменные к «статическим» переменным, но они совсем не совпадают
    2012-11-10 23: 07: 01Z
  3. В частности, вы говорите, что "статически размещенные локальные переменные" размещены в стеке. На самом деле они расположены в сегменте данных. Только автоматически распределенные переменные (которые включают в себя большинство, но не все локальные переменные, а также такие вещи, как параметры функции, передаваемые по значению, а не по ссылке), выделяются в стеке.
    2012-11-11 01: 44: 52Z
  4. Я только что понял, что вы правы - в C статическое распределение - это отдельная вещь, а не термин для всего, что не является динамический . Я отредактировал свой ответ, спасибо.
    2012-11-12 00: 29: 15Z
  5. Это не просто C. Java, Pascal, Python и многие другие имеют понятия статическое и автоматическое, а не динамическое распределение. Сказать «статическое распределение» означает везде одно и то же. Ни в одном языке статическое распределение не означает «не динамический». Вы хотите использовать термин «автоматическое» распределение для того, что вы описываете (то есть вещи в стеке).
    2012-11-12 17: 16: 28Z

(Я переместил этот ответ из другого вопроса, который был более или менее обманчивым.)

Ответ на ваш вопрос зависит от конкретной реализации и может варьироваться в зависимости от компилятора и архитектуры процессора. Однако здесь приведено упрощенное объяснение.

  • И стек, и куча являются областями памяти, выделенными из базовой операционной системы (часто это виртуальная память, которая по требованию сопоставляется с физической памятью).
  • В многопоточной среде каждый поток будет иметь свой собственный полностью независимый стек, но они будут совместно использовать кучу. Параллельный доступ должен контролироваться в куче и невозможен в стеке.

Куча

  • Куча содержит связанный список использованных и свободных блоков. Новые выделения в куче (по new или malloc) выполняются путем создания подходящего блока из одного из свободных блоков. Это требует обновления списка блоков в куче. Эта метаинформация о блоках в куче также хранится в куче часто в небольшой области перед каждым блоком.
  • По мере роста кучи новые блоки часто распределяются от более низких адресов к более высоким адресам. Таким образом, вы можете думать о куче как о куче блоков памяти, размер которых увеличивается по мере выделения памяти. Если куча слишком мала для выделения, размер часто может быть увеличен путем получения большего объема памяти из-подЛежащая операционная система.
  • Распределение и освобождение многих небольших блоков может оставить кучу в состоянии, когда между используемыми блоками расположено много небольших свободных блоков. Запрос на выделение большого блока может потерпеть неудачу, поскольку ни один из свободных блоков не является достаточно большим, чтобы удовлетворить запрос на выделение, даже если объединенный размер свободных блоков может быть достаточно большим. Это называется фрагментация кучи .
  • Когда используемый блок, который находится рядом со свободным блоком, освобождается, новый свободный блок может быть объединен со смежным свободным блоком, чтобы создать больший свободный блок, эффективно уменьшающий фрагментацию кучи.

Куча

стек

  • Стек часто работает в тесном тандеме со специальным регистром на процессоре, который называется указатель стека . Первоначально указатель стека указывает на вершину стека (самый высокий адрес в стеке).
  • Процессор имеет специальные инструкции для помещения значений в стек и извлечения их обратно из стека. Каждый push сохраняет значение в текущем местоположении указателя стека и уменьшает указатель стека. pop извлекает значение, на которое указывает указатель стека, а затем увеличивает указатель стека (не смущайтесь тем фактом, что добавление значения в стек уменьшает указатель стека и удаляя значение увеличивает . Помните, что стек увеличивается до дна). Сохраненные и извлеченные значения являются значениями регистров ЦП.
  • Когда вызывается функция, центральный процессор использует специальные инструкции, которые передают текущий указатель инструкций , то есть адрес кода, выполняемого в стеке. Затем процессор переходит к функции, устанавливая указатель инструкции на адрес вызываемой функции. Позже, когда функция возвращается, старый указатель инструкций извлекается из стека, и выполнение возобновляется в коде сразу после вызова функции.
  • При вводе функции указатель стека уменьшается, чтобы выделить больше места в стеке для локальных (автоматических) переменных. Если функция имеет одну локальную 32-битную переменную, в стеке выделяются четыре байта. Когда функция возвращается, указатель стека перемещается назад, чтобы освободить выделенную область.
  • Если у функции есть параметры, они помещаются в стек перед вызовом функции. Код в функции затем может перемещаться вверх по стеку от текущего указателя стека, чтобы найти эти значения.
  • Вызовы вложенных функций работают как шарм. Каждый новый вызов будет выделять параметры функции, адрес возврата и пространство для локальных переменных, и эти записи активации могут быть сложены для вложенных вызовов и будут корректно разматываться при возврате функций.
  • Поскольку стек является ограниченным блоком памяти, вы можете вызвать переполнение стека , вызвав слишком много вложенных функций и /или выделив слишком много места для локальных переменных. Часто область памяти, используемая для стека, настраивается таким образом, что запись ниже дна (самый низкий адрес) стека вызовет ловушку или исключение в ЦП. Это исключительное условие может быть перехвачено средой выполнения и преобразовано в какое-то исключение переполнения стека.

Стек

  

Может ли функция размещаться в куче вместо стека?

Нет, записи активации для функций (то есть локальных или автоматических переменных) размещаются в стеке, который используется не только для хранения этих переменных, но и для отслеживания вызовов вложенных функций.

Как управлять кучей, действительно зависит от среды выполнения. C использует malloc, а C ++ использует new, но во многих других языках есть сборка мусора.

Однако стек является более низкоуровневой функцией, тесно связанной с архитектурой процессора. Растить кучу, когда не хватает места, не так уж сложно, так как это может быть реализовано в вызове библиотеки, которая обрабатывает кучу. Однако увеличение стека часто невозможно, так как переполнение стека обнаруживается только тогда, когда это слишком поздно; и закрытие потока выполнения - единственно возможный вариант.

    
695
2012-03-28 23: 03: 30Z
  1. @ Martin - Очень хороший ответ /объяснение, чем более абстрактный принятый ответ. Пример программы сборки, показывающий указатели /регистры стека, используемые для вызовов функций vis, будет более наглядным.
    2012-04-25 16: 42: 17Z
  2. Каждый ссылочный тип является композицией типов значений (int, string и т. д.). Как уже говорилось, эти типы значений хранятся в стеке, чем то, как это работает, когда они являются частью ссылочного типа.
    2014-02-15 10: 26: 30Z
  3. Этот ответ был, на мой взгляд, лучшим, потому что он помог мне понять, что на самом деле является оператором возврата и как он связан с этим «обратным адресом», с которым я сталкиваюсь каждый время от времени, что означает помещать функцию в стек и почему функции помещаются в стек. Отличный ответ!
    2014-03-19 19: 59: 21Z
  4. Это лучшее, на мой взгляд, а именно упоминание того, что куча /стек очень зависят от реализации. Другие ответы предполагают много вещей о языке и среде /ОС. +1
    2014-11-29 02: 00: 15Z
  5. Что вы имеете в виду "Код в функции может затем перемещаться вверх по стеку от текущего указателя стека, чтобы найти эти значения". ? Можете ли вы уточнить это, пожалуйста?
    2015-05-03 09: 36: 25Z

В следующем коде C #

 
public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Вот как осуществляется управление памятью

Изображение переменных в стеке

Local Variables, который должен длиться до тех пор, пока вызов функции идет в стек. Куча используется для переменных, время жизни которых мы на самом деле не знаем заранее, но мы ожидаем, что они продлятся некоторое время. В большинстве языков важно, чтобы мы знали во время компиляции, насколько велика переменная, если мы хотим сохранить ее в стеке.

Объекты (которые меняются по размеру по мере их обновления) попадают в кучу, потому что мы не знаем во время создания, как долго они будут длиться. Во многих языках куча мусора собирается для поиска объектов (таких как объект cls1), на которые больше нет ссылок.

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

Дополнительную информацию можно найти здесь:

Разница между стеком и выделение кучи памяти «timmurphy.org

и здесь:

Создание объектов в стеке и куче

Эта статья является источником рисунка выше: Шесть важных концепций .NET: стек, куча, типы значений, ссылочные типы, упаковка и распаковка - CodeProject

но учтите, что в нем могут быть некоторые неточности.

    
388
2018-03-07 10: 27: 28Z
  1. Это неверно. i и cls не являются "статическими" переменными. они называются «локальными» или «автоматическими» переменными. Это очень важное различие. См. [Link] stackoverflow.com/a/13326916/1763801 для получения разъяснений
    2012-11-10 23: 05: 05Z
  2. Я не говорил, что они были статическими переменными . Я сказал, что int и cls1 являются статическими items . Их память статически распределена, и поэтому они идут в стек. Это в отличие от объекта, который требует динамического выделения памяти и поэтому находится в куче.
    2012-11-20 14: 38: 13Z
  3. Я цитирую "Статические элементы ... идти в стек". Это просто неправильно. Статические элементы помещаются в сегмент данных, автоматические элементы помещаются в стек.
    2012-11-21 16: 55: 41Z
  4. Кроме того, тот, кто написал эту статью о codeproject, не знает, о чем говорит. Например, он говорит, что« примитивным нужна память статического типа », что совершенно не соответствует действительности. Ничто не мешает вам выделить примитивы в динамически куча, просто напишите что-то вроде "int array [] = new int [num]" и вуаля, примитивы, динамически выделяемые в .NET. Это только одна из нескольких неточностей.
    2012-11-21 17: 02: 46Z
  5. Я отредактировал ваше сообщение, потому что вы допустили серьезные технические ошибки относительно того, что происходит в стеке и куче.
    2014-09-09 23: 09: 12Z

Стек Когда вы вызываете функцию, аргументы этой функции плюс некоторые другие накладные расходы помещаются в стек. Некоторая информация (например, куда идти по возвращении) также хранится там. Когда вы объявляете переменную внутри вашей функции, эта переменная также размещается в стеке.

Распределение стека довольно просто, потому что вы всегда освобождаете в обратном порядке, в котором вы распределяете. Стек вещи добавляются при вводе функций, соответствующие данные удаляются при выходе из них. Это означает, что вы склонны оставаться в небольшой области стека, если не вызываете множество функций, которые вызывают множество других функций (или не создаете рекурсивное решение).

Куча Куча - это общее имя для размещения данных, которые вы создаете на лету. Если вы не знаете, сколько космических кораблей собирается создать ваша программа, вы, вероятно, будете использовать оператор new (или malloc или эквивалентный) для создания каждого космического корабля. Это распределение останется на некоторое время, поэтому, скорее всего, мы освободим вещи в другом порядке, чем мы их создали.

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

Реализация Реализация как стека, так и кучи обычно зависит от времени выполнения /ОС. Часто игры и другие приложения, критически важные для производительности, создают свои собственные решения для памяти, которые захватывают большой кусок памяти из кучи и затем распределяют ее изнутри, чтобы не полагаться на ОС для памяти.

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

Физическое расположение в памяти Это менее актуально, чем вы думаете, из-за технологии под названием виртуальной памяти , которая заставляет вашу программу думать, что вы иметь доступ к определенному адресу, где физические данные находятся где-то еще (даже на жестком диске!). Адреса, которые вы получаете для стека, растут по мере того, как дерево вызовов становится глубже. Адреса для кучи непредсказуемы (т. Е. Специфичны для имплиментации) и, честно говоря, не важны.

    
197
2008-09-17 04: 34: 33Z
  1. Рекомендация избегать использования кучи довольно сильна. Современные системы имеют хорошие диспетчеры кучи, а современные динамические языки широко используют кучу (без особого беспокойства программиста). Я бы сказал, используйте кучу, но с ручным распределителем, не забудьте освободить!
    2008-09-17 04: 31: 42Z
  2. Если вы можете использовать стек или кучу, используйте стек. Если вы не можете использовать стек, на самом деле нет выбора. Я использую как много, так и, конечно, используя std :: vector или аналогичные хиты. Для новичка вы избегаете кучи, потому что стек просто так прост!
    2008-09-17 04: 35: 51Z
  3. Если ваш язык не реализует сборку мусора, интеллектуальные указатели (объекты, выделенные по отдельности, обернутые вокруг указателя, которые выполняют подсчет ссылок для динамически выделяемых фрагментов памяти), тесно связаны между собой на сборку мусора и достойный способуправление кучей безопасным и без утечек. Они реализованы в различных средах, но их не так сложно реализовать и для собственных программ.
    2016-10-11 19: 10: 40Z
  4. "Вот почему следует избегать кучи (хотя она все еще часто используется)." Я не уверен, что это означает на практике, тем более что во многих языках высокого уровня память управляется по-разному. Поскольку этот вопрос помечен как независимый от языка, я бы сказал, что этот конкретный комментарий /строка неуместен и неприменим.
    2018-07-25 09: 34: 14Z
  5. Хороший вопрос @JonnoHampson - Хотя вы делаете правильное замечание, я бы сказал, что если вы работаете на «языке высокого уровня» с GC, вы, вероятно, не наденете не заботятся о механизмах выделения памяти - и поэтому даже не заботятся о том, что такое стек и куча.
    2018-07-26 22: 22: 52Z

Чтобы уточнить, этот ответ содержит неверную информацию ( Томас исправил свой ответ после комментариев, круто :)). Другие ответы просто не объясняют, что означает статическое распределение. Итак, ниже я объясню три основных формы распределения и их связь с кучей, стеком и сегментом данных. Я также покажу некоторые примеры на C /C ++ и Python, чтобы помочь людям понять.

«Статические» (статически размещенные AKA) переменные не размещаются в стеке. Не думайте так - многие люди делают только потому, что «статический» очень похож на «стек». Они на самом деле не существуют ни в стеке, ни в куче. Они являются частью того, что называется сегмент данных .

Однако, как правило, лучше рассматривать " scope " и " время жизни ", а не "stack" и "heap".

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

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

Хотя большинство компиляторов и интерпретаторов реализуют это поведение аналогичным образом с точки зрения использования стеков, куч и т. д., компилятор может иногда нарушать эти соглашения, если он хочет, пока поведение корректно. Например, из-за оптимизации локальная переменная может существовать только в регистре или может быть полностью удалена, даже если большинство локальных переменных существует в стеке. Как было отмечено в нескольких комментариях, вы можете свободно реализовать компилятор, который даже не использует стек или кучу, а вместо этого использует некоторые другие механизмы хранения (редко делается, так как стеки и кучи отлично подходят для этого).

Я приведу простой аннотированный C-код для иллюстрации всего этого. Лучший способ научиться - это запускать программу под отладчиком и наблюдать за ее поведением. Если вы предпочитаете читать Python, перейдите к концу ответа:)

 
// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Особенно яркий пример того, почему важно различать время жизни и область видимости, состоит в том, что переменная может иметь локальную область видимости, но статическое время жизни - например, «someLocalStaticVariable» в приведенном выше примере кода. Такие переменные могут сделать наши общие, но неформальные привычки именования очень запутанными. Например, когда мы говорим « локальный », мы обычно имеем в виду « локально распределенную переменную с автоматически распределенной областью », а когда мы говорим «глобально», мы обычно имеем в виду « статически распределенную переменную с глобальной областью действия ». К сожалению, когда дело доходит до таких вещей, как " статически распределенные переменные в области файла ", многие просто говорят ... " ху ??? ".

Некоторые из вариантов синтаксиса в C /C ++ усугубляют эту проблему - например, многие думают, что глобальная изменчивостьОни не являются "статичными" из-за синтаксиса, показанного ниже.

 
int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Обратите внимание, что добавление ключевого слова "static" в объявлении выше предотвращает глобальную область действия var2. Тем не менее, глобальное var1 имеет статическое размещение. Это не интуитивно понятно! По этой причине я стараюсь никогда не использовать слово «статический» при описании области действия, а вместо этого говорю что-то вроде «файл» или «ограниченный файл» область действия. Однако многие люди используют фразу «статический» или «статический объем» для описания переменной, доступ к которой возможен только из одного файла кода. В контексте времени жизни «static» всегда означает, что переменная выделяется при запуске программы и освобождается при выходе из программы.

Некоторые люди считают эти понятия специфичными для C /C ++. Они не. Например, приведенный ниже пример Python иллюстрирует все три типа размещения (в интерпретируемых языках возможны некоторые тонкие различия, о которых я не буду здесь говорить).

 
from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones
    
178
2017-07-30 12: 13: 36Z
  1. Я бы сослался на статическую переменную, объявленную внутри функции как имеющую только локальную доступность , но, как правило, не использовал бы термин «область действия» с Это. Кроме того, возможно, стоит отметить, что один аспект стека /кучи, с которым языки имеют практически нулевую гибкость: язык, который сохраняет контекст выполнения в стеке, не может использовать тот же стек для хранения вещей, которые должны будут переживать контексты, в которых они созданы. , Некоторые языки, такие как PostScript, имеют несколько стеков, но имеют «кучу», которая ведет себя больше как стек.
    2013-12-09 21: 53: 25Z
  2. @ supercat Все это имеет смысл. Я определил область видимости как «какие части кода могут обращаться к переменной» (и чувствую, что это наиболее стандартное определение), поэтому я думаю, что мы согласны:)
    2013-12-17 20: 28: 57Z
  3. Я бы считал, что "область действия" переменной ограничена временем и пространством. Переменная в области видимости объекта класса должна содержать свое значение, пока объект существует. Переменная внутри контекста контекста выполнения должна содержать свое значение, пока выполнение остается в этом контексте. Объявление статической переменной создает идентификатор , область действия которого ограничена текущим блоком, который присоединяется к переменной , область действия которой не ограничена.
    2013-12-17 20: 57: 52Z
  4. @ supercat Вот почему я использую слово время жизни, как я называю то, что вы называете временной областью. Это уменьшает необходимость перегружать слово «охват» таким большим количеством значений. Насколько я могу судить, по-видимому, нет полного консенсуса по точным определениям, даже среди канонических источников. Моя терминология взята частично из K & R и частично из преобладающего использования на первом отделении CS, в котором я учился /преподавал. Всегда приятно услышать другое осознанное мнение.
    2013-12-28 22: 50: 47Z
  5. вы, должно быть, шутите. Вы действительно можете определить статическую переменную внутри функции?
    2017-05-16 09: 57: 26Z

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

  1. Стек и куча не обязательно должны быть единичными. Распространенная ситуация, когда у вас есть более одного стека, если у вас есть более одного потока в процессе. В этом случае каждый поток имеет свой собственный стек. Вы также можете иметь более одной кучи, например, некоторые конфигурации DLL могут привести к тому, что разные DLL будут выделяться из разных куч, поэтому обычно плохая идея освобождать память, выделенную другой библиотекой.

  2. В C вы можете воспользоваться преимуществами распределения переменной длины, используя alloca , который выделяет в стеке, в отличие от alloc, который выделяет в куче. Эта память не сохранится в вашем операторе возврата, но она полезна для чистого буфера.

  3. Создание огромного временного буфера в Windows, которым вы не пользуетесь, не является бесплатным. Это потому, что компler сгенерирует цикл проверки стека, который вызывается каждый раз при входе в вашу функцию, чтобы убедиться, что стек существует (поскольку Windows использует одну защитную страницу в конце стека, чтобы определить, когда ей нужно увеличить стек. Если вы обращаетесь к памяти более одной страницы с конца стека у вас вылетит). Пример: р>

 
void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}
    
161
2017-07-30 11: 18: 35Z
  1. Re "в отличие от alloc": Вы имеете в виду "в отличие от malloc"?
    2017-07-30 11: 19: 36Z
  2. Насколько переносимым является alloca?
    2017-07-30 11: 20: 14Z
  3. @ PeterMortensen это не POSIX, переносимость не гарантируется.
    2017-11-01 17: 38: 27Z

Другие прямо ответили на ваш вопрос, но, пытаясь понять стек и кучу, я думаю, что было бы полезно рассмотреть структуру памяти традиционного процесса UNIX (без потоков и распределителей на основе mmap()). Глоссарий управления памятью содержит диаграмму этого макета памяти.

Стек и куча традиционно расположены на противоположных концах виртуального адресного пространства процесса. При доступе к стеку размер автоматически увеличивается до размера, установленного ядром (который можно настроить с помощью setrlimit(RLIMIT_STACK, ...)). Куча увеличивается, когда распределитель памяти вызывает системный вызов brk() или sbrk(), отображая больше страниц физической памяти в виртуальное адресное пространство процесса.

В системах без виртуальной памяти, таких как некоторые встроенные системы, часто применяется одна и та же базовая схема, за исключением того, что размер стека и кучи фиксирован. Однако в других встроенных системах (например, основанных на микроконтроллерах PIC Microchip) программный стек представляет собой отдельный блок памяти, который не может быть адресован инструкциями перемещения данных, и может быть изменен или прочитан только косвенно с помощью инструкций потока программы (вызов, возврат и т. д.). Другие архитектуры, такие как процессоры Intel Itanium, имеют несколько стеков . В этом смысле стек является элементом архитектуры ЦП.

    
128
2015-08-05 20: 36: 31Z

Стек - это часть памяти, которой можно манипулировать с помощью нескольких инструкций языка ассемблера, таких как «pop» (удалить и вернуть значение из стека) и «push» (передать значение в стек), но Также вызов (вызов подпрограммы - это подталкивает адрес для возврата в стек) и возврат (возврат из подпрограммы - извлечение адреса из стека и переход к нему). Это область памяти ниже регистра указателя стека, который может быть установлен по мере необходимости. Стек также используется для передачи аргументов подпрограммам, а также для сохранения значений в регистрах перед вызовом подпрограмм.

Куча - это часть памяти, которая выделяется приложению операционной системой, обычно через системный вызов типа malloc. В современных ОС эта память представляет собой набор страниц, к которым имеет доступ только вызывающий процесс.

Размер стека определяется во время выполнения и, как правило, не увеличивается после запуска программы. В программе на Си стек должен быть достаточно большим, чтобы вместить каждую переменную, объявленную внутри каждой функции. Куча будет динамически расти по мере необходимости, но ОС в конечном итоге сделает вызов (она часто будет увеличивать кучу более чем на значение, запрошенное malloc, так что по крайней мере некоторым будущим malloc не придется возвращаться к ядру для получить больше памяти. Такое поведение часто настраивается)

Поскольку вы выделили стек перед запуском программы, вам никогда не понадобится malloc, прежде чем вы сможете использовать этот стек, так что это небольшое преимущество. На практике очень сложно предсказать, что будет быстрым, а что медленным, в современных операционных системах с подсистемами виртуальной памяти, потому что страницыРеализовано и где они хранятся - это детали реализации.

    
109
2008-09-17 04: 29: 20Z
  1. Также стоит упомянуть, что Intel сильно оптимизирует доступ к стеку, особенно такие вещи, как прогнозирование места возврата из функции.
    2008-09-17 04: 37: 38Z

Я думаю, что многие другие люди дали вам в основном правильные ответы по этому вопросу.

Однако, одна деталь, которая была упущена, заключается в том, что «куча» на самом деле должна называться «бесплатным хранилищем». Причина этого различия заключается в том, что исходное бесплатное хранилище было реализовано со структурой данных, известной как «биномиальная куча». По этой причине выделение из ранних реализаций malloc () /free () было выделением из кучи. Однако в наши дни большинство бесплатных магазинов реализованы с очень сложными структурами данных, которые не являются биномиальными кучами.

    
109
2008-09-17 04: 57: 51Z
  1. Другой пример: большинство ответов (слегка) подразумевают, что для использования языка C требуется использование "стека". Это распространенное заблуждение, хотя это (безусловно) доминирующая парадигма для реализации C99 6.2.4 automatic storage duration objects (переменных). Фактически, слово «стек» даже не встречается в стандарте языка C99: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
    2009-09-01 05: 03: 55Z
  2. [@ Heath] У меня небольшой комментарий к вашему ответу. Посмотрите на принятый ответ на этот вопрос а>. В нем говорится, что бесплатный магазин , скорее всего, такой же, как куча , хотя не обязательно.
    2012-02-12 06: 34: 39Z

Что такое стек?

Стек - это куча объектов, обычно аккуратно расположенных.

 Введите описание изображения здесь

  
    

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

  

Что такое куча?

Куча - это неопрятная коллекция беспорядочно скопившихся вещей.

 Введите описание изображения здесь

  
    

В вычислительных архитектурах куча - это область динамически выделяемой памяти, которая автоматически управляется операционной системой или библиотекой диспетчера памяти.
    Память в куче выделяется, освобождается и регулярно изменяется во время выполнения программы, что может привести к проблеме, называемой фрагментацией.
    Фрагментация происходит, когда объектам памяти выделяются небольшие промежутки между ними, которые слишком малы для хранения дополнительных объектов памяти.
    Чистый результат - это процент пространства кучи, который не может быть использован для дальнейшего выделения памяти.

  

Оба вместе

  
    

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

  

Что быстрее?стек или куча? И почему?

  
    

Стек намного быстрее, чем куча.
    Это связано с тем, как память выделяется в стеке.
    Выделение памяти в стеке так же просто, как перемещение указателя стека вверх.

  

Для новичков в программировании, вероятно, будет хорошей идеей использовать стек, поскольку он проще.
Поскольку стек небольшой, его можно использовать, когда вы точно знаете, сколько памяти вам понадобится для ваших данных, или если вы знаете, что размер ваших данных очень мал.
Лучше использовать кучу, когда вы знаете, что вам понадобится много памяти для ваших данных, или вы просто не уверены, сколько памяти вам понадобится (например, с динамическим массивом).

Модель памяти Java

 Введите описание изображения здесь

Стек - это область памяти, в которой хранятся локальные переменные (включая параметры метода). Когда дело доходит до объектных переменных, это просто ссылки (указатели) на реальные объекты в куче.
Каждый раз, когда создается экземпляр объекта, часть памяти кучи откладывается для хранения данных (состояния) этого объекта. Поскольку объекты могут содержать другие объекты, некоторые из этих данных могут фактически содержать ссылки на эти вложенные объекты.

    
105
2017-07-30 12: 16: 46Z

Со стеком можно делать некоторые интересные вещи. Например, у вас есть такие функции, как alloca (при условии, что вы можете обойти обильные предупреждения, касающиеся его use), которая является формой malloc, которая специально использует стек, а не кучу памяти.

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

    
87
2009-03-19 15: 55: 06Z
  1. Насколько переносимым является alloca? Например, это работает на Windows? Это только для Unix-подобных операционных систем?
    2017-07-30 12: 00: 54Z

Просто в стеке создаются локальные переменные. Кроме того, каждый раз, когда вы вызываете подпрограмму, счетчик программы (указатель на следующую машинную инструкцию) и любые важные регистры, а иногда и параметры помещаются в стек. Затем любые локальные переменные внутри подпрограммы помещаются в стек (и используются оттуда). Когда подпрограмма заканчивается, все эти вещи возвращаются из стека. Персональный компьютер и регистр данных получаются и возвращаются туда, где они были, как сейчас, чтобы ваша программа могла продолжать свой веселый путь.

Куча - это область памяти, из которой создаются динамические выделения памяти (явные вызовы «new» или «allocate»). Это специальная структура данных, которая может отслеживать блоки памяти разных размеров и их статус распределения.

В «классических» системах ОЗУ было расположено так, что указатель стека начинался с нижней части памяти, указатель кучи начинался сверху, и они росли навстречу друг другу. Если они перекрываются, у вас недостаточно оперативной памяти. Это не работает с современными многопоточными ОС, хотя. Каждый поток должен иметь свой собственный стек, и они могут создаваться динамически.

    
85
2009-03-19 15: 19: 30Z
  1. [@ T.E.D.] Почему вы сказали "иногда параметры помещаются в стек"? Что я знаю, так это то, что они всегда . Могпожалуйста, уточните подробнее?
    2012-02-12 06: 36: 42Z
  2. @ OmarOthman - я говорю это, потому что все зависит от автора вашего компилятора /интерпретатора, что происходит при вызове подпрограммы. Классическое поведение Фортрана - не использовать стек вообще. Некоторые языки поддерживают такие экзотические вещи, как передача по имени, которая фактически является текстовой заменой.
    2012-04-03 15: 57: 07Z

Из WikiAnwser.

Stack

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

Эта цепочка приостановленных вызовов функций является стеком, поскольку элементы в стеке (вызовы функций) зависят друг от друга.

Стек важно учитывать при обработке исключений и выполнении потоков.

Heap

Куча - это просто память, используемая программами для хранения переменных. Элемент кучи (переменных) не имеет зависимостей друг от друга и всегда может быть доступен случайным образом в любое время.

    
79
2017-07-30 12: 01: 46Z
  1. "Мне нравится принятый ответ лучше, поскольку он еще более низкого уровня." Это плохо, а не хорошо.
    2017-04-25 09: 02: 17Z

Stack

  • Очень быстрый доступ
  • Не нужно явно отменять распределение переменных
  • Пространство эффективно управляется процессором, память не фрагментируется
  • Только локальные переменные
  • Ограничение размера стека (зависит от ОС)
  • Размер переменных не может быть изменен

Heap

  • Переменные могут быть доступны глобально
  • Нет ограничений на объем памяти
  • (относительно) более медленный доступ
  • Нет гарантированного эффективного использования пространства, со временем память может фрагментироваться, поскольку блоки памяти выделяются, а затем освобождаются.
  • Вы должны управлять памятью (вы отвечаете за распределение и освобождение переменных)
  • Размер переменных можно изменить с помощью realloc ()
50
2017-07-30 12: 14: 32Z

ОК, просто и коротко говоря, они означают упорядоченный и не упорядоченный ...!

Стек : в элементах стека вещи оказываются друг на друге, что означает, что они будут быстрее и эффективнее обрабатываться! ...

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

Куча : нет порядка, обработка будет медленнее, а значения перепутаны без какого-либо определенного порядка или индекса ... они случайные, и между ними нет взаимосвязи ... поэтому выполнение и время использования может быть разным ...

Я также создаю изображение ниже, чтобы показать, как они могут выглядеть:

 введите описание изображения здесь

    
42
2018-04-23 02: 06: 25Z

Короче говоря

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

Подробно

Стек

Стек - это структура данных «LIFO» (последний пришел, первый вышел), которая управляется и оптимизируется центральным процессором довольно близко. Каждый раз, когда функция объявляет новую переменную, она «помещается» в стек. Затем при каждом выходе из функции все переменныепомещенные в стек этой функцией, освобождаются (то есть они удаляются). После освобождения переменной стека эта область памяти становится доступной для других переменных стека.

Преимущество использования стека для хранения переменных заключается в том, что память управляется за вас. Вам не нужно выделять память вручную или освобождать ее, если она вам больше не нужна. Более того, поскольку процессор эффективно организует стековую память, чтение и запись в переменные стека выполняется очень быстро.

Подробнее можно найти здесь . р>

Куча

Куча - это область памяти вашего компьютера, которая не управляется автоматически и не так жестко управляется процессором. Это более свободно плавающая область памяти (и больше). Чтобы выделить память в куче, вы должны использовать malloc () или calloc (), которые являются встроенными функциями C. После выделения памяти в куче вы отвечаете за использование free () для освобождения этой памяти, когда она вам больше не нужна.

Если вы этого не сделаете, ваша программа будет иметь то, что известно как утечка памяти. То есть память в куче все равно будет выделена (и не будет доступна другим процессам). Как мы увидим в разделе отладки, есть инструмент под названием Valgrind , который может помочь вам обнаружить память утечки.

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

В отличие от стека, переменные, созданные в куче, доступны любой функции в любой точке вашей программы. Переменные кучи по сути являются глобальными по объему.

Подробнее можно найти здесь .

Переменные, размещенные в стеке, сохраняются непосредственно в памяти, и доступ к этой памяти очень быстрый, и его распределение решается при компиляции программы. Когда функция или метод вызывает другую функцию, которая, в свою очередь, вызывает другую функцию и т. Д., Выполнение всех этих функций приостанавливается до тех пор, пока самая последняя функция не вернет свое значение. Стек всегда резервируется в порядке LIFO, последний зарезервированный блок всегда является следующим блоком, который должен быть освобожден. Это упрощает отслеживание стека, освобождение блока из стека - всего лишь настройка одного указателя.

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

 Введите описание изображения здесь

Вы можете использовать стек, если точно знаете, сколько данных вам нужно выделить до компиляции, и он не слишком велик. Вы можете использовать кучу, если не знаете точно, сколько данных вам понадобится во время выполнения или если вам нужно выделить много данных.

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

Каждый поток получает стек, хотя обычно для приложения имеется только одна куча (хотя нередко иметь несколько куч для разных типов размещения).

 Введите описание изображения здесь

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

Более подробная информация приведена здесь и здесь .

Теперь перейдите к ответам на ваш вопрос .

  

В какой степени они контролируются операционной системой или языковой средой выполнения?

ОС выделяет стек для каждого потока системного уровня при его создании. Обычно ОС вызывается языковой средой выполнения для выделения кучи для приложения.

Более подробную информацию можно найти здесь .

  

Каков их охват?

Уже указано сверху.

  

"Вы можете использовать стек, если точно знаете, сколько данных вам нужно выделить до компиляции, и он не слишком велик. Вы можете использовать кучу, если точно не знаете, сколько данных вам понадобится время выполнения или если вам нужно выделить много данных. "

Дополнительные сведения можно найти в здесь .

  

От чего зависит размер каждого из них?

Размер стека устанавливается ОС при создании потока. Размер кучи устанавливается при запуске приложения, но может увеличиваться по мере необходимости (распределитель запрашивает больше памяти у операционной системы).

  

Что делает его быстрее?

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

Кроме того, стек и куча - это не только вопрос производительности; он также многое говорит об ожидаемом времени жизни объектов.

Подробности можно найти в здесь .

    
40
2018-12-09 15: 12: 15Z

В 1980-х годах UNIX распространялся как кролики, а крупные компании катались самостоятельно. У Exxon был такой же, как и утерянные в истории десятки брендов. Распределение памяти было оставлено на усмотрение многих разработчиков.

Типичная программа на С была разложена в памяти возможность увеличения путем изменения значения brk (). Как правило, HEAP была чуть ниже этого значения BRK и увеличение brk увеличило количество доступной кучи.

Один STACK обычно был областью ниже HEAP, которая была частью памяти не содержащий ничего ценного до вершины следующего фиксированного блока памяти. Этот следующий блок часто был КОДОМ, который мог быть перезаписан данными стека в одном из знаменитых хаков своей эпохи.

Один типичный блок памяти был BSS (блок нулевых значений) который был случайно не обнулен в предложении одного производителя. Другой был DATA, содержащий инициализированные значения, включая строки и числа. Третьим был код, содержащий CRT (время выполнения C), main, функции и библиотеки.

Появление виртуальной памяти в UNIX меняет многие ограничения. Нет никаких объективных причин, почему эти блоки должны быть смежными, или фиксированного размера, или заказал определенным образом сейчас. Конечно, до UNIX были Multics, которые не страдали от этих ограничений. Вот схема, показывающая один из макетов памяти той эпохи.

Типичная схема памяти программ в стиле UNIX C 1980-х годов

    
35
2015-03-27 19: 55: 51Z

стек , куча и данные каждого процесса в виртуальной памяти:

 стек, куча и статические данные

    
29
2018-03-12 18: 00: 15Z

Пара центов: я думаю, будет хорошо нарисовать память графически и более просто:

 Это мое видение записки процессаry конструкция с упрощением для более легкого понимания, что происходит


Стрелки - показывают, где растут стек и куча, размер стека процесса имеет ограничение, определенное в ОС, ограничения размера стека потока по параметрам в API создания потока обычно. Обычно размер кучи ограничивается максимальным размером виртуальной памяти процесса, например, для 32-разрядных 2-4 ГБ.

Очень простой способ: куча процесса является общей для процесса и всех потоков внутри, используя для распределения памяти в общем случае что-то вроде malloc () .

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

    
24
2017-07-30 12: 22: 02Z

Так как некоторые ответы стали придирками, я собираюсь внести свою лепту.

Удивительно, но никто не упомянул, что несколько (т.е. не относящихся к количеству запущенных потоков уровня ОС) стеков вызовов можно найти не только в экзотических языках (PostScript) или платформах (Intel Itanium), но и в fiber , зеленые темы и некоторые реализации сопрограммы .

Волокна, зеленые нити и сопрограммы во многом похожи, что приводит к путанице. Разница между волокнами и зелеными нитями заключается в том, что первые используют кооперативную многозадачность, в то время как последние могут иметь либо кооперативную, либо вытесняющую (или даже обе). Различия между волокнами и сопрограммами см. В здесь. . р>

В любом случае назначение обоих волокон, зеленых нитей и сопрограмм - одновременное выполнение нескольких функций, но не параллельно (см. этот SO вопрос для различий) в одном потоке уровня ОС, организованно передавая управление взад и вперед друг от друга . р>

При использовании волокон, зеленых нитей или сопрограмм у вас обычно есть отдельный стек для каждой функции. (Технически, для каждой функции используется не просто стек, а целый контекст выполнения. Самое главное, регистры ЦП.) Для каждого потока существует столько стеков, сколько имеется одновременно выполняемых функций, и поток переключается между выполнением каждой функции. в соответствии с логикой вашей программы. Когда функция запускается до конца, ее стек уничтожается. Таким образом, количество и время жизни стеков являются динамическими, а не определяется количеством потоков на уровне ОС!

Обратите внимание, что я сказал, что обычно имеет отдельный стек для каждой функции ". Существуют как stackful , так и stackless реализации процедур. Наиболее известными стековыми реализациями C ++ являются Boost.Coroutine и Microsoft PPL async/await. (Однако возобновляемые функции C ++ (ака "async и await"), которые были предложены для C ++ 17, скорее всего будут использовать сопрограммы без стеков.)

Предлагается предложение Fibers для стандартной библиотеки C ++. Кроме того, есть некоторые сторонние библиотеки . Зеленые темы чрезвычайно популярны в таких языках, как Python и Ruby.

    
21
2017-05-23 11: 55: 01Z

Мне есть чем поделиться, хотя основные моменты уже рассмотрены.

Стек

  • Очень быстрый доступ.
  • Хранится в оперативной памяти.
  • Здесь загружаются вызовы функций, а также передаются локальные переменные и параметры функций.
  • Пространство освобождается автоматически, когда программа выходит из области видимости.
  • Хранится в последовательной памяти.
Heap
  • Низкий доступ к стеку.
  • Хранится в оперативной памяти.
  • Здесь хранятся динамически создаваемые переменные, что позже потребует освобождения выделенной памяти после использования.
  • Хранится везде, где выполняется выделение памяти, всегда доступен по указателю.

Интересная заметка:

  • Если бы вызовы функций были сохранены в куче, это привело бы к двум беспорядочным точкам:
    1. Благодаря последовательному хранению в стеке выполнение выполняется быстрее. Хранение в куче привело бы к огромному расходу времени, что замедлило бы выполнение всей программы.
    2. Если бы функции хранились в куче (грязное хранилище, на которое указывает указатель), не было бы никакого способа вернуться к адресу вызывающего абонента назад (какой стек дает из-за последовательного хранения в памяти).
14
2018-12-10 08: 23: 58Z
  1. краткий и чистый. хорошо:)
    2018-12-05 19: 14: 07Z

Многие ответы верны как понятия, но мы должны отметить, что аппаратному обеспечению необходим аппаратный стек (например, микропроцессор), чтобы разрешить вызов подпрограмм (CALL на ассемблере ..). (ООП, ребята, будут называть это методами )

В стеке вы сохраняете обратные адреса, а вызов → push /ret → pop управляется непосредственно на оборудовании.

Вы можете использовать стек для передачи параметров ... даже если он медленнее, чем использование регистров (скажет гуру микропроцессора или хорошая книга по BIOS 1980-х годов ...)

  • Без стека нет микропроцессор может работать. (мы не можем представить программу, даже на ассемблере, без подпрограмм /функций)
  • Без кучи это может. (Программа на языке ассемблера может работать без, так как куча - это понятие ОС, как malloc, то есть вызов OS /Lib.

Использование стека быстрее, чем:

  • Это аппаратное обеспечение, и даже push /pop очень эффективны.
  • malloc требует входа в режим ядра, использования блокировки /семафора (или других примитивов синхронизации), выполнения некоторого кода и управления некоторыми структурами, необходимыми для отслеживания распределения.
8
2017-08-07 08: 27: 43Z
  1. Что такое OPP? Вы имеете в виду ООП ( объектно-ориентированное_программирование )?
    2017-07-30 12: 34: 40Z
  2. Вы хотите сказать, что malloc - это вызов ядра?
    2017-07-30 12: 35: 56Z
  3. 1) да, извините .. ООП ... 2) malloc: я пишу в ближайшее время, извините ... malloc находится в пространстве пользователя .. но может вызвать другие звонки .... дело в том, что использование кучи МОЖЕТ быть очень медленным ...
    2017-08-03 12: 18: 19Z

Ничего себе! Так много ответов, и я не думаю, что один из них понял это правильно ...

1) Где и что они (физически в памяти реального компьютера)?

Стек - это память, которая начинается с самого высокого адреса памяти, выделенного для образа вашей программы, и затем уменьшается в этом значении. Он зарезервирован для параметров вызываемой функции и для всех временных переменных, используемых в функциях.

Есть две кучи: открытая и закрытая.

Частная куча начинается с 16-байтовой границы (для 64-битных программ) или 8-байтовой границы (для 32-битных программ) после последнего байта кода в вашей программе, а затем увеличивается в значении оттуда , Это также называется кучей по умолчанию.

Если частная куча становится слишком большой, она перекрывает область стека, как и стек, если она становится слишком большой. Поскольку стек начинается с более высокого адреса и работает до более низкого адреса, при правильном взломе вы можете сделать стек настолько большим, что он будет переполнять область приватной кучи и перекрывать область кода. Хитрость в том, чтобы перекрыть достаточно кодаобласть, которую вы можете подключить к коду. Это немного сложно сделать, и вы рискуете сбой программы, но это легко и очень эффективно.

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

2) В какой степени они контролируются ОС или языковой средой выполнения?

Стек управляется программистом, частная куча управляется ОС, а общедоступная куча никем не контролируется, потому что это служба ОС - вы делаете запросы, и они либо удовлетворяются, либо отклоняются. р>

2b) Какова их сфера применения?

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

2c) От чего зависит размер каждого из них?

Размер стека и частной кучи определяется параметрами времени выполнения вашего компилятора. Общая куча инициализируется во время выполнения с помощью параметра размера.

2d) Что делает человека быстрее?

Они не предназначены для быстрой работы, они предназначены для того, чтобы быть полезными. То, как их использует программист, определяет, являются ли они «быстрыми» или «медленными»

REF:

https://norasandler.com/2019/02/18 /Write-а-Compiler-10.html

https://docs.microsoft. ком /EN-US /окна /настольные /API /heapapi /NF-heapapi-GetProcessHeap

https://docs.microsoft. ком /EN-US /окна /настольные /API /heapapi /NF-heapapi-heapcreate

    
8
2019-02-20 02: 04: 38Z
источник размещен Вот