Что и где находится стек и куча? - PullRequest
7640 голосов
/ 17 сентября 2008

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

  • Где и что они (физически в памяти реального компьютера)?
  • В какой степени они контролируются ОС или языком исполнения?
  • Каков их охват?
  • От чего зависит размер каждого из них?
  • Что делает человека быстрее?

Ответы [ 25 ]

24 голосов
/ 17 декабря 2015

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

This is my vision of process memory construction with simplification for more easy understanding wht happening


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

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

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

21 голосов
/ 02 марта 2015

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

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

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

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

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

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

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

14 голосов
/ 15 ноября 2017

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

Стек

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

Heap

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

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

  • Если бы вызовы функций были сохранены в куче, это привело бы к двум беспорядочным точкам:
    1. Благодаря последовательному хранению в стеке выполнение выполняется быстрее. Хранение в куче привело бы к огромным затратам времени и замедлению работы всей программы.
    2. Если бы функции хранились в куче (грязное хранилище, на которое указывает указатель), не было бы никакого способа вернуться к адресу вызывающей стороны обратно (какой стек дает последовательное хранение в памяти).
8 голосов
/ 20 февраля 2019

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

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

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

Есть две кучи: публичная и приватная.

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

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

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

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

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

2b) Каков их охват?

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

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

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

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

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

REF:

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

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate

8 голосов
/ 28 июля 2017

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

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

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

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

Использование стека происходит быстрее:

  • Это аппаратное обеспечение, и даже push / pop очень эффективны.
  • malloc требует входа в режим ядра, использования блокировки / семафора (или других примитивов синхронизации), выполнения некоторого кода и управления некоторыми структурами, необходимыми для отслеживания распределения.
...