Что и где стопка и куча?

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

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

person mattshane    schedule 17.09.2008    source источник
comment
действительно хорошее объяснение можно найти здесь Что разница между стеком и кучей?   -  person Songo    schedule 16.12.2013
comment
Также (действительно) хорошо: codeproject. com / Articles / 76153 / (часть стека / кучи)   -  person Ben    schedule 15.02.2014
comment
youtube.com/watch?v=clOUdVDDzIM&spfreload=5   -  person Selvamani    schedule 11.06.2016
comment
По теме см. Stack Clash. Исправления Stack Clash затронули некоторые аспекты системных переменных и поведения, например rlimit_stack. См. Также проблему 1463241 Red Hat.   -  person jww    schedule 21.06.2017
comment
@mattshane Определения стека и кучи вообще не зависят от типов значений и ссылок. Другими словами, стек и куча могут быть полностью определены, даже если типы значений и ссылочные типы никогда не существовали. Кроме того, для понимания типов значений и ссылок стек - это просто деталь реализации. Автор Эрик Липперт: Стек - это деталь реализации, часть первая.   -  person Matthew    schedule 13.11.2017
comment
Непонятно в ответах: для языковой среды выполнения (например, .NET) существует один стек на поток для управления вызовами методов / локальными переменными и только одна куча, установленная для всех процессов среды выполнения. Куча контролируется сборщиком мусора. Стеки времени выполнения / пространство кучи - это часть непрерывной виртуальной памяти, выделяемой ОС для процессов (которая сама загружается физическими блоками ОЗУ в произвольном порядке) по запросу процессов. Путаница вокруг стеков связана с существованием в компьютере множества видов стеков, не связанных со стеками времени выполнения языка. Стек - это просто структура хранения LIFO.   -  person mins    schedule 05.07.2019
comment
Если вы хотите увидеть симуляцию того, как выглядят стек и куча во время выполнения программы на C, попробуйте C Репетитор.   -  person user2846495    schedule 04.05.2020


Ответы (28)


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

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

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

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

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

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

Каковы их возможности?

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

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

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

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

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

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

person Jeff Hill    schedule 17.09.2008
comment
Хороший ответ, но я думаю, вы должны добавить, что, хотя стек выделяется ОС при запуске процесса (при условии существования ОС), он поддерживается встроенной программой. Это еще одна причина, по которой стек работает быстрее - операции push и pop обычно представляют собой одну машинную инструкцию, и современные машины могут выполнять как минимум 3 из них за один цикл, тогда как выделение или освобождение кучи включает вызов кода ОС. - person sqykly; 08.10.2013
comment
Меня очень смущает диаграмма в конце. Я думал, что понял, пока не увидел это изображение. - person Sina Madani; 15.08.2016
comment
@Anarelle процессор выполняет инструкции с операционной системой или без нее. Мне очень близок пример SNES, в котором не было вызовов API, не было ОС в том виде, в каком мы ее знаем сегодня, но у нее был стек. Выделение в стеке - это сложение и вычитание в этих системах, и это нормально для переменных, уничтожаемых, когда они выталкиваются путем возврата из функции, которая их создала, но ограничьте это, скажем, конструктором, результат которого не может быть просто выброшенный. Для этого нам понадобится куча, которая не привязана к вызову и возврату. У большинства ОС есть куча API-интерфейсов, нет причин делать это самостоятельно - person sqykly; 13.10.2016
comment
stack - это память, отведенная под временное пространство. Прохладный. Но где это на самом деле отложено с точки зрения структуры памяти Java ?? Это память кучи / память без кучи / другое (структура памяти Java согласно betsol.com/2017/06/) - person Jatin Shashoo; 22.07.2018
comment
@JatinShashoo Среда выполнения Java, как интерпретатор байт-кода, добавляет еще один уровень виртуализации, так что то, что вы упомянули, является всего лишь точкой зрения приложения Java. С точки зрения операционной системы все это просто куча, в которой процесс времени выполнения Java выделяет часть своего пространства как память без кучи для обрабатываемого байт-кода. Остальная часть этой кучи уровня ОС используется как куча уровня приложения, где хранятся данные объекта. - person kbec; 06.09.2018
comment
Итак, стек - это куча в терминах уровня ОС. Стек управляется непосредственно потоком с использованием инструкций ЦП, а куча - это область обычной памяти, отображаемая ОС. - person kbec; 06.09.2018
comment
Привет. Я думаю, что в вашем сообщении (что в остальном здорово) четко не говорится, что куча медленнее, чем стек, только когда вы СОЗДАЕТЕ материал (объекты). Когда вы ДОСТУПНЫ к материалу, куча должна иметь примерно такую ​​же скорость, что и стек. - person Z80; 26.04.2019
comment
@sqykly Было бы правильно называть старые игровые ОС сами по себе? У них есть своего рода графический интерфейс, они управляют низкоуровневым вводом-выводом и имеют полный доступ ко всему оборудованию, а также работают как процесс корневого уровня. Именно по этим причинам я всегда видел их такими, и даже такие новые консоли, как Wii, действительно запускали только одну вещь за раз: меню HOME было встроено в каждую игру, а не предоставлялось основной ОС. (главное меню), которое вообще не работает в фоновом режиме. За исключением новых консолей, большинство старых игр, кажется, исполняют роль ОС. - person Jacob Pritchett; 13.05.2019
comment
@SinaMadani, хахаха, и здесь. - person Peregring-lk; 13.08.2019
comment
Хороший ответ. Но вы должны сказать, что стек также быстрее, потому что он может храниться в регистре процессора, а не в памяти. - person Pouriya; 27.08.2019
comment
Я понял то, о чем вы говорите в первых двух абзацах, когда я изучил архитектуру MIPS и что на самом деле стек / куча (регистры) / как память распределяется в стеке и куча на низком уровне, то есть сборка / машина язык. Я думаю, что когда мы изучаем низкий уровень, мы получаем более точное определение «как» / «когда» что-то происходит с кучей и стеком. - person ; 16.11.2019
comment
Еще несколько примечаний, которые могут иметь отношение к читателям: 1. блок управления памятью (MMU) сопоставляет адреса физической памяти в таблице страниц с виртуальными адресами. 2. Стек полезен для поддержания непрерывной локальности памяти, где данные в куче могут быть распределены между несколькими устройствами памяти, такими как ОЗУ, кэш L1, кэш L2 и т. Д. 3 рассмотреть возможность хранения фиксированного массива из 10 элементов , каждый бок о бок в памяти в стеке, итерация / поиск элемента тривиальны. Для 10 элементов в связанном списке в куче может потребоваться поиск не только в другом блоке памяти, но и в разных устройствах памяти. - person catleeball; 31.08.2020
comment
@SinaMadani - Схема показывает, что локальные переменные и методы зарезервированы в стеке, но они ссылаются на объекты, размещенные в куче. - person unqualified; 21.11.2020
comment
На диаграмме видно, что локальные переменные в стеке ссылаются только на объекты в куче. Возможно ли, чтобы локальные переменные также ссылались на объекты в стеке? Если я, например, выполняю int x = 5; внутри вызова функции в C, разве это 5 не хранится в стеке? - person Danny; 31.12.2020

Стек:

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

Куча:

  • Хранится в оперативной памяти компьютера точно так же, как в стеке.
  • В 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;
person Brian R. Bondy    schedule 17.09.2008
comment
Указатель pBuffer и значение b находятся в стеке и, скорее всего, выделяются на входе в функцию. В зависимости от компилятора буфер также может быть выделен при входе в функцию. - person Andy; 19.03.2009
comment
Распространенное заблуждение, что язык 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 < / а> - person johne; 01.09.2009
comment
@Brian Вы должны объяснить почему buffer [] и указатель pBuffer создаются в стеке и почему данные pBuffer создаются в куче. Я думаю, что некоторые люди могут быть сбиты с толку вашим ответом, поскольку они могут подумать, что программа специально указывает, что память выделяется в стеке, а не в куче, но это не так. Это потому, что Buffer - это тип значения, тогда как pBuffer - ссылочный тип? - person Howiecamp; 08.02.2010
comment
@Hershi: Re 1. Это уже покрыто: переменные, созданные в стеке, выйдут из области видимости и автоматически освободятся. По поводу 2. Это уже рассмотрено: Переменные в куче должны быть уничтожены вручную и никогда не выпадают из области видимости. Данные освобождаются с помощью delete, delete [] или free - person Brian R. Bondy; 05.04.2010
comment
Ответственность за утечки памяти - Heaps не несут ответственности за утечки памяти! Ленивые / забывчивые / бывшие java-кодеры / кодеры, которым наплевать, - это! - person Laz; 25.03.2013
comment
Также неверны комментарии по поводу области действия и распределения - область действия вообще не связана со стеком или кучей. Переменные в куче должны быть уничтожены вручную и никогда не выпадают из области видимости. неверно; правильнее было бы сказать, что данные в куче не освобождаются, когда переменные, которые на них ссылаются, выходят за пределы области видимости. Вы (или сборщик мусора) должны освободить их. - person Orion Edwards; 01.09.2014
comment
Прочтите другой ответ Память компьютера - это просто набор адресов; куча и стек - изобретения компилятора. это правда? - person Vineeth Chitteti; 24.12.2014
comment
• Вы могли бы использовать кучу, если не знаете точно, сколько данных вам понадобится во время выполнения, или если вам нужно выделить много данных. Также для управления временем жизни объекта - person Björn Hallström; 29.07.2015
comment
@ BrianR.Bondy Привет, я сомневаюсь, что, если я выделил память в куче и не освободил ее. Так будет ли эта память освобождена после завершения программы? - person ; 23.08.2015
comment
Итак, стеки и кучи - это абстракции, очевидно созданные с использованием реальных структур данных. Но вам не хватает одного момента: кто создает и отвечает за жизненный цикл стека и кучи? Это операционная система? И как именно создаются эти стек и куча? Если вы не хотите включать эту информацию непосредственно в свой ответ, не могли бы вы дать ссылку на книгу или источник, где это подробно объясняется? Кроме того, ваши перечисления могли бы быть лучше, а точки перечислений можно было бы упорядочить более логичным способом! - person nbro; 15.03.2016
comment
@RohitSaluja Большинство операционных систем освобождают неосвобожденную память при завершении программы, но все же хороший способ - это явно освободить всю динамически выделяемую память. - person Spikatrix; 22.04.2016
comment
Сборщик мусора НЕ управляет стеками. Поэтому любое выделение или освобождение памяти должно выполняться вручную. - person user1789573; 01.08.2016
comment
Один незначительный / особый случай, регистрируется в процессоре - для компилятора C / C ++, если обнаружено, что переменная стека имеет очень короткое время жизни или код может быть оптимизирован соответствующим образом, переменная может никогда не попасть в то, что пользователь может думать об этом как о памяти, но он должен находиться действительно под рукой к процессору благодаря его собственному небольшому количеству рабочих регистров. Это часто имеет место для значений, используемых для индексации в массиве вещей, потому что, особенно для процессоров со сложным набором инструкций, они имеют быстрые / эффективные операционные коды, которые используют эти регистры для этого типа операции! - person SlySven; 23.09.2016

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

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

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

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

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

    Куча, как куча лакричного ассорти

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

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

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

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

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

  • Каков их объем?

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

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

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

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

  • Что делает человека быстрее?

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

person thomasrutter    schedule 19.03.2009
comment
Неправильный образ стопки; это должно быть что-то вроде термобокса .co.uk / images / stories / FiniW /, поэтому его также называют «выталкивающим стеком». - person David Conrad; 10.08.2012
comment
Дэвид: Я не согласен с тем, что это хорошее изображение или что «выталкивающий стек» - хороший термин для иллюстрации концепции. Когда вы добавляете что-то в стек, другое содержимое стека не сдвигается вниз, оно остается на месте. - person thomasrutter; 13.08.2012
comment
В этом ответе есть большая ошибка. Статические переменные не размещаются в стеке. См. Мой ответ [ссылка] stackoverflow.com/a/13326916/1763801 для разъяснения. вы приравниваете автоматические переменные к статическим, но они совсем не одно и то же - person davec; 11.11.2012
comment
В частности, вы говорите, что статически распределенные локальные переменные размещаются в стеке. Фактически они размещены в сегменте данных. В стеке выделяются только автоматически выделяемые переменные (которые включают большинство, но не все локальные переменные, а также такие вещи, как параметры функции, передаваемые по значению, а не по ссылке). - person davec; 11.11.2012
comment
Когда я сказал статически распределенные локальные переменные, я имел в виду в отличие от динамически распределенных. Если вы запускаете функцию с int a;, a попадет в стек. Если вы запустите функцию с int *a = (int *) malloc(32768*sizeof(int));, тогда содержимое этого массива не будет в стеке, поскольку оно выделяется динамически. - person thomasrutter; 12.11.2012
comment
Я только что понял, что вы правы - в C статическое распределение - это отдельная вещь, а не термин для всего, что не является динамическим. Я отредактировал свой ответ, спасибо. - person thomasrutter; 12.11.2012
comment
Это не только C. Java, Pascal, Python и многие другие имеют понятия статического, автоматического и динамического выделения. Сказать, что статическое распределение означает одно и то же практически везде. Ни на одном языке статическое распределение не означает не динамическое. Вам нужен термин автоматическое выделение для того, что вы описываете (то есть для вещей в стеке). - person davec; 12.11.2012
comment
и если вы хотите увидеть это в действии на python вместо c, я добавил несколько примеров кода ниже :) - person davec; 12.11.2012
comment
все ли воспоминания спроектированы таким образом, со стеком и кучей? - person Timeless; 17.11.2013
comment
@Timeless stack и heap - два распространенных способа распределения памяти, которые можно найти практически на любой вычислительной платформе. Тем не менее, существуют и другие концепции распределения памяти, некоторые из которых работают на более низких (отображение физической памяти в виртуальную) или более высоких (структуры данных) уровнях. - person thomasrutter; 18.11.2013
comment
@thomasrutter спасибо, а кто решил, как мы должны распределять память таким образом? конструкторы оборудования? - person Timeless; 18.11.2013
comment
@Timeless Практические причины. Стек работает таким образом, потому что это очень быстро и просто для процессора, и он соответствует нашим парадигмам локальных переменных, выдаваемых при входе в функцию и выталкиваемых при выходе и т. Д. Кучи работают таким образом, потому что нам нужен способ случайного распределения и удаления -распределение памяти или произвольной длины без необходимости в том, чтобы она была последовательной и без необходимости освобождать ее в обратном порядке, в котором она была выделена. - person thomasrutter; 18.11.2013
comment
@Timeless CUDA - это пример, в котором использование стека и кучи категорически не рекомендуется. Существуют тысячи потоков, и иметь стек для каждого из них очень дорого. По этой причине большинство функций устройства встроены, и следует избегать рекурсии. Операции с кучей, такие как malloc, также возможны, но не рекомендуется из соображений эффективности. Предпочтительный подход - выделить всю необходимую память один раз, а затем просто использовать / повторно использовать ее без какого-либо выделения / освобождения. - person CygnusX1; 10.07.2016
comment
Мне больше всего понравился этот ответ. Одна только стопка бумаг помогла мне сформировать связь с тем, что происходит, так что спасибо за использование простых аналогий. - person theJerm; 05.03.2020

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

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

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

Куча

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

Куча

Стек

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

Стек

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

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

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

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

person Martin Liversage    schedule 31.07.2009
comment
@Martin - очень хороший ответ / объяснение, чем более абстрактный принятый ответ. Пример программы на ассемблере, показывающий указатели / регистры стека, используемые для вызовов функций, был бы более наглядным. - person Bikal Lem; 25.04.2012
comment
Каждый ссылочный тип является составом типов значений (int, string и т. Д.). Как говорится, эти типы значений хранятся в стеке, а не как они работают, когда они являются частью ссылочного типа. - person Nps; 15.02.2014
comment
Этот ответ был, на мой взгляд, лучшим, потому что он помог мне понять, что на самом деле представляет собой оператор return и как он соотносится с этим адресом возврата, с которым я время от времени сталкиваюсь, что значит поместить функцию в стек и почему функции помещаются в стеки. Отличный ответ! - person Alex; 19.03.2014
comment
На мой взгляд, это лучший вариант, а именно для упоминания того, что куча / стек очень специфичны для реализации. Другие ответы предполагают много вещей о языке и среде / ОС. +1 - person Qix - MONICA WAS MISTREATED; 29.11.2014
comment
Что вы имеете в виду? После этого код функции может перемещаться вверх по стеку от текущего указателя стека, чтобы найти эти значения. ? Не могли бы вы подробнее рассказать об этом? - person Koray Tugay; 03.05.2015
comment
@KorayTugay: когда функция начинает выполнение, вершина стека (самый низкий адрес, потому что стек инвертирован) содержит адрес возврата (ret). Аргументы функции (args) находятся ниже этого в стеке (по более высокому адресу). Объявление аргументов функции определяет расположение аргументов в стеке, и код функции может получить доступ к этим аргументам, зная только указатель стека при вводе функции, а также точный размер и последовательность аргументов. Все относительно указателя стека. - person Martin Liversage; 03.05.2015
comment
@MartinLiversage Я думал, что к ним обращаются по индексам массива. - person Koray Tugay; 03.05.2015
comment
@KorayTugay: Мое объяснение касается уровня ЦП / машинного кода / сборки, где есть только регистры и память, которые могут быть адресованы для чтения и записи данных. В общем, массивы представляют собой концепцию более высокого уровня (языка программирования). Хорошо, у ЦП есть векторные инструкции, но они не важны, когда вы пытаетесь понять стек. - person Martin Liversage; 03.05.2015
comment
@MartinLiversage Хорошо, спасибо за разъяснения. Я пытаюсь создать простой интерпретатор для языка (для учебных целей), поэтому я запутался. - person Koray Tugay; 03.05.2015
comment
Интересно, сколько современных программистов не использовали шестнадцатеричный редактор? Если вы не можете разбить двоичные файлы, чтобы понять, что с ними происходит, тогда концепции стеков и куч станут забытыми. Не все способны программировать на ассемблере, и это даже выходит за рамки моих навыков программирования, но знание концепций машинной архитектуры и того, как работают наборы инструкций, жизненно важно для овладения навыками. - person Francis Cugler; 06.05.2015
comment
(продолжение) ... Современные программисты с более высоким уровнем абстракции похожи на современных пилотов, которые зависят от систем автоматической навигации: когда эта система выходит из строя и им приходится полагаться на карты, они теряются. - person Francis Cugler; 06.05.2015
comment
Отличное объяснение некоторых концепций, но эти диаграммы действительно сбивают с толку. - person Sina Madani; 15.08.2016
comment
Что означает «адрес возврата» функции в записях активации в стеке - person Hakim; 14.11.2018
comment
@Hakim: адрес возврата - это текущий указатель инструкции перед вызовом. Я пытаюсь объяснить это в третьем пункте в разделе Стек. Перед вызовом функции текущий указатель инструкции помещается в стек. Когда функция возвращает этот адрес, он извлекается из стека, и выполнение возобновляется со следующей инструкции. Запись активации вызова функции содержит адрес возврата, по которому ЦП должен продолжить выполнение после завершения функции. Функция этого не знает. Он должен быть предоставлен вызывающей функцией. - person Martin Liversage; 14.11.2018
comment
@Nps: Насколько мне известно, значения ссылочного типа также сохраняются в куче. В противном случае куча в основном состояла бы из указателей, ссылающихся на данные в стеке. - person Hakim; 14.11.2018
comment
@MartinLiversage: Таким образом, стек вызовов фактически сохраняется в стеке через различные записи активации, каждая из которых представляет функцию, не так ли? - person Hakim; 14.11.2018
comment
@Hakim: Стек вызовов - это зарезервированная память, используемая для реализации вызовов функций. Каждый вызов создает новую запись активации в стеке. Запись активации содержит 1) аргументы, предоставленные функции, 2) условия, в которых выполнение будет продолжено после завершения функции, и 3) локальные переменные функции. Записи активации накладываются друг на друга (отсюда и название stack), что позволяет многократно вызывать одну и ту же функцию (рекурсия). Когда функция возвращает, ее запись активации отбрасывается, а вызывающая функция и ее запись активации становятся активными. См. Диаграмму 2. - person Martin Liversage; 14.11.2018

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

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

Вот как управляется память

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

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

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

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

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

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

и тут:

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

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

но имейте в виду, что он может содержать некоторые неточности.

person Snowcrash    schedule 09.11.2012
comment
Это неверно. i и cls не являются статическими переменными. они называются локальными или автоматическими переменными. Это очень важное различие. См. [Ссылка] stackoverflow.com/a/13326916/1763801 для уточнения. - person davec; 11.11.2012
comment
Я не сказал, что это статические переменные. Я сказал, что int и cls1 - статические items. Их память выделяется статически, и поэтому они отправляются в стек. Это отличается от объекта, которому требуется динамическое выделение памяти, которое, следовательно, хранится в куче. - person Snowcrash; 20.11.2012
comment
Цитирую Статические предметы ... уходят в стопку. Это просто неправильно. Статические элементы помещаются в сегмент данных, автоматические элементы помещаются в стек. - person davec; 21.11.2012
comment
Также тот, кто написал эту статью о кодпроекте, не знает, о чем он говорит. Например, он говорит, что примитивным нужна память статического типа, что совершенно неверно. Ничто не мешает вам динамически размещать примитивы в куче, просто напишите что-нибудь вроде int array [] = new int [num] и вуаля, примитивы размещаются динамически в .NET. Это лишь одна из нескольких неточностей. - person davec; 21.11.2012
comment
Если вы посмотрите мою репутацию, вы увидите, что я еще не могу проголосовать против. Так что это даже не я голосую против. - person davec; 21.11.2012
comment
@Snow Crash, в вашем ответе в начале This helps: ссылка битая. Пожалуйста, исправьте это. Спасибо. - person sree; 02.04.2014
comment
Какой инструмент вы использовали, чтобы нарисовать это изображение? - person Vagish; 09.06.2014
comment
Я отредактировал ваш пост, потому что вы допустили серьезные технические ошибки в отношении того, что находится в стеке и куче. - person Tom Leys; 10.09.2014
comment
@SnowCrash один вопрос о вашей фотографии - как мне получить доступ к i после выделения y? Мне нужно выскакивать y? Поменять их местами? Что, если их разделяет множество локальных переменных? - person confused00; 16.01.2015
comment
@ confused00 У вас еще есть ответ на этот вопрос? Я думаю, что все они хранятся в массиве, подобном структуре, и во время компиляции они помещаются в стек операндов, верно? - person Koray Tugay; 03.05.2015
comment
@ confused00 Ни то, ни другое - потому что это не стеки. Это фреймы стека вызовов. Каждый кадр можно просто катать как угодно. Тогда еще одно неточное / вводящее в заблуждение описание! - person Lightness Races in Orbit; 13.01.2020

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

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

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

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

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

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

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

person Tom Leys    schedule 17.09.2008
comment
Рекомендация избегать использования кучи довольно сильна. Современные системы имеют хорошие менеджеры кучи, а современные динамические языки широко используют кучу (без особого беспокойства программиста об этом). Я бы сказал, используйте кучу, но с ручным распределителем не забудьте освободить! - person Greg Hewgill; 17.09.2008
comment
Если вы можете использовать стек или кучу, используйте стек. Если вы не можете использовать стек, выбора действительно нет. Я часто использую и то, и другое, и, конечно же, std :: vector или аналогичный попадает в кучу. Новичку лучше избегать кучи, потому что стек - это просто! - person Tom Leys; 17.09.2008
comment
Если ваш язык не реализует сборку мусора, интеллектуальные указатели (отдельные объекты, которые обертывают указатель, который выполняет подсчет ссылок для динамически выделяемых фрагментов памяти) тесно связаны со сборкой мусора и являются достойным способом управления кучей в безопасном режиме. и без утечек. Они реализованы в различных фреймворках, но их также не так сложно реализовать в ваших собственных программах. - person BenPen; 11.10.2016
comment
Вот почему следует избегать кучи (хотя она все еще часто используется). Я не уверен, что это означает на практике, тем более что память на многих языках высокого уровня управляется по-разному. Поскольку этот вопрос помечен как независимый от языка, я бы сказал, что этот конкретный комментарий / строка неуместны и неприменимы. - person LintfordPickle; 25.07.2018
comment
Хорошее замечание @JonnoHampson - хотя вы и говорите правильно, я бы сказал, что если вы работаете на языке высокого уровня с GC, вам, вероятно, вообще не нужны механизмы распределения памяти - и поэтому даже все равно что такое стек и куча. - person Tom Leys; 27.07.2018

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

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

Однако обычно лучше рассматривать «область и время жизни, а не« стек »и« кучу ».

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

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

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

Я предоставлю простой аннотированный код на языке 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 в приведенном выше примере кода. Такие переменные могут сбить с толку наши обычные, но неформальные привычки именования. Например, когда мы говорим «локальная», мы обычно имеем в виду «автоматически выделяемая переменная с локальной областью действия», а когда мы говорим «глобальная», мы обычно имеем в виду «статически выделенная переменная с глобальной областью действия < / em> ". К сожалению, когда дело доходит до таких вещей, как «статически размещенные переменные с областью видимости файла», многие люди просто говорят… «да ???».

Некоторые варианты синтаксиса в 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 имеет статическое размещение. Это не интуитивно! По этой причине я стараюсь никогда не использовать слово «статический» при описании области видимости, а вместо этого говорю что-то вроде области «файл» или «ограниченный файл». Однако многие люди используют фразу «статическая» или «статическая область видимости» для описания переменной, к которой можно получить доступ только из одного файла кода. В контексте времени жизни «статический» всегда означает, что переменная выделяется при запуске программы и освобождается при выходе из программы.

Некоторые люди считают эти концепции специфичными для 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
person Community    schedule 10.11.2012
comment
Я бы назвал статическую переменную, объявленную внутри функции, имеющей только локальную доступность, но, как правило, не использовал бы с ней термин «область действия». Кроме того, может быть стоит отметить, что один аспект стека / кучи, с которым языки имеют практически нулевую гибкость: язык, который сохраняет контекст выполнения в стеке, не может использовать тот же стек для хранения вещей, которые должны будут пережить контексты, в которых они созданы . Некоторые языки, такие как PostScript, имеют несколько стеков, но имеют кучу, которая больше похожа на стек. - person supercat; 10.12.2013
comment
@supercat Все это имеет смысл. Я определил область видимости как то, какие части кода могут обращаться к переменной (и считаю, что это наиболее стандартное определение), поэтому я думаю, что мы согласны :) - person davec; 18.12.2013
comment
Я считаю, что область действия переменной ограничена не только пространством, но и временем. Переменная в области класса-объекта должна сохранять свое значение, пока существует объект. Переменная в области контекста выполнения должна сохранять свое значение, пока выполнение остается в этом контексте. Объявление статической переменной создает идентификатор, область действия которого ограничена текущим блоком, который присоединяется к переменной, область действия которой неограничена. - person supercat; 18.12.2013
comment
@supercat Вот почему я использую слово «время жизни», как я называю то, что вы называете временной областью. Это уменьшает необходимость перегружать объем слова таким количеством значений. Насколько я могу судить, похоже, что нет единого мнения относительно точных определений, даже среди канонических источников. Моя терминология частично заимствована из K&R, а частично из преобладающего использования на первом отделении CS, на котором я учился / преподавал. Всегда приятно услышать еще одно информированное мнение. - person davec; 29.12.2013
comment
Один очень распространенный источник путаницы заключается в том, относится ли переменная к идентификатору, месту хранения или (для объектно-ориентированных структур) объекту кучи, который идентифицируется текущим содержимым места хранения. Автоматические переменные - это места хранения, которые возникают, когда выполнение входит в определенную область, и перестают существовать впоследствии. К статическим переменным можно обращаться только по имени в пределах их области видимости, но во многих языках функция может возвращать указатель на статическую переменную, а для кода в любом контексте, который имеет копию этой указатель... - person supercat; 29.12.2013
comment
... чтобы иметь возможность получить доступ к переменной по желанию, независимо от того, имеет ли какой-либо аспект текущего контекста выполнения какое-либо отношение к функции, в которой была определена переменная. - person supercat; 29.12.2013
comment
ты должно быть шутишь. вы действительно можете определить статическую переменную внутри функции? - person Zaeem Sattar; 16.05.2017
comment
@zaeemsattar абсолютно, и это не обычно видеть в коде C - person davec; 23.06.2017
comment
@ZaeemSattar Думайте о статической переменной функции как о скрытом глобальном или как о частной статической переменной-члене. - person Tom Leys; 27.07.2018

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

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

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

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

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}
person Don Neufeld    schedule 17.09.2008
comment
Re в отличие от alloc: Вы имеете в виду в отличие от malloc? - person Peter Mortensen; 30.07.2017
comment
Насколько портативен alloca? - person Peter Mortensen; 30.07.2017
comment
@PeterMortensen, это не POSIX, переносимость не гарантируется. - person Don Neufeld; 01.11.2017

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

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

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

person bk1e    schedule 17.09.2008

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

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

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

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

person Daniel Papasian    schedule 17.09.2008
comment
Также здесь стоит упомянуть, что Intel сильно оптимизирует доступ к стеку, особенно такие вещи, как предсказание того, куда вы вернетесь из функции. - person Tom Leys; 17.09.2008

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

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

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

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

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

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

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

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

Оба вместе

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

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

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

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

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

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

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

person Shreyos Adikari    schedule 11.06.2014

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

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

person Community    schedule 17.09.2008
comment
Еще одна придирка - большинство ответов (слегка) подразумевают, что использование стека требуется C языком. Это распространенное заблуждение, хотя это (безусловно) доминирующая парадигма для реализации C99 6.2.4 automatic storage duration objects (переменных). Фактически, стек слов даже не появляется в C99 языковом стандарте: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf - person johne; 01.09.2009
comment

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

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

person Peter    schedule 19.03.2009
comment
Насколько портативен alloca? Например, работает ли он в Windows? Только для Unix-подобных операционных систем? - person Peter Mortensen; 30.07.2017

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

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

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

person T.E.D.    schedule 19.03.2009
comment
[@ T.E.D.] Почему вы сказали, что иногда параметры помещаются в стек? Я знаю, что они всегда. Не могли бы вы подробнее рассказать? - person OmarOthman; 12.02.2012
comment
@OmarOthman - я говорю это, потому что это полностью зависит от автора вашего компилятора / интерпретатора, что происходит при вызове подпрограммы. Классическое поведение Fortran - вообще не использовать стек. Некоторые языки поддерживают такие экзотические вещи, как передача по имени, которая фактически является текстовой заменой. - person T.E.D.; 03.04.2012

Из WikiAnwser.

Куча

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

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

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

Куча

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

person devXen    schedule 02.04.2009
comment
Мне больше нравится принятый ответ, поскольку он еще более низкий. Это плохо, а не хорошо. - person Lightness Races in Orbit; 25.04.2017

Стек

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

Куча

  • Доступ к переменным возможен глобально
  • Нет ограничений на размер памяти
  • (Относительно) более медленный доступ
  • Нет гарантированного эффективного использования пространства, память со временем может фрагментироваться, поскольку блоки памяти выделяются, а затем освобождаются.
  • Вы должны управлять памятью (вы отвечаете за выделение и освобождение переменных)
  • Размер переменных можно изменять с помощью realloc ()
person unknown    schedule 30.01.2014

Суммируя

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


В деталях

Стек

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

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

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


Куча

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

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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

Каковы их возможности?

Уже занесено в топ.

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

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

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

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

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

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

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

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

person Abrar Jahin    schedule 02.05.2016

ОК, просто и коротко они означают заказано и не заказано ...!

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

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

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

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

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

person Alireza    schedule 18.07.2017

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

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

person Yousha Aleayoub    schedule 14.09.2017

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

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

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

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

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

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

person jlettvin    schedule 27.03.2015

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

«Это


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

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

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

person Maxim Akristiniy    schedule 17.12.2015

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

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

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

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

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

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

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

person shakurov    schedule 02.03.2015

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

Стек

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

Куча

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

Интересное примечание:

  • Should the function calls had been stored in heap, it would had resulted in 2 messy points:
    1. Due to sequential storage in stack, execution is faster. Storage in heap would have resulted in huge time consumption thus making the whole program execute slower.
    2. Если бы функции хранились в куче (беспорядочное хранилище, указанное указателем), не было бы возможности вернуться к адресу вызывающего абонента обратно (который дает стек из-за последовательного хранения в памяти).
person pkthapa    schedule 15.11.2017
comment
лаконично и чисто. отлично:) - person ingconti; 05.12.2018

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

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

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

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

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

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

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

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

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

2b) Каков их объем?

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

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

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

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

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

ССЫЛКА:

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-heapi-heapcreate

person ar18    schedule 20.02.2019

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

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

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

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

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

  • Есть аппаратное обеспечение, и даже push / pop очень эффективны.
  • malloc требует входа в режим ядра, использования блокировок / семафоров (или других примитивов синхронизации), выполнения некоторого кода и управления некоторыми структурами, необходимыми для отслеживания распределения.
person ingconti    schedule 27.07.2017
comment
Что такое OPP? Вы имеете в виду ООП (объектно-ориентированное_программирование)? - person Peter Mortensen; 30.07.2017
comment
Вы хотите сказать, что malloc это вызов ядра? - person Peter Mortensen; 30.07.2017
comment
1) да, извините ... ООП ... 2) malloc: я пишу вкратце, извините ... malloc находится в пользовательском пространстве ... но может запускать другие вызовы ... дело в том, что использование кучи МОЖЕТ быть очень медленным ... - person ingconti; 03.08.2017
comment
Многие ответы правильны как концепции, но мы должны отметить, что стек необходим аппаратному обеспечению (то есть микропроцессору), чтобы разрешить вызов подпрограмм (CALL на языке ассемблера ..). Вы путаете стек ЦП (если он есть в современном ЦП) и стеки времени выполнения языка (по одному на поток). Когда программисты говорят о стеке, это стек выполнения потока среды выполнения, например стек потока NET), мы не говорим о стеке ЦП. - person mins; 05.07.2019
comment
NET - это не настоящий стек. (то же самое для JVM): это концепции SW. (другие называют эту запись активации) Мы должны начать с реальных схем, как в истории ПК, чтобы получить реальное понимание. - person ingconti; 19.07.2020

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

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

Источник: Academind

person nCardot    schedule 22.03.2020

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

Все современные процессоры работают с одной и той же теорией микропроцессоров: все они основаны на так называемых регистрах, а некоторые предназначены для стека для повышения производительности. У всех процессоров с самого начала есть стековые регистры, и, как я знаю, они всегда были здесь, так сказать. Языки ассемблера одинаковы с самого начала, несмотря на различия ... вплоть до Microsoft и его промежуточного языка (IL), который изменил парадигму, чтобы иметь язык ассемблера виртуальных машин OO. Так что в будущем мы сможем иметь некоторый процессор CLI / CIL (один проект MS).

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

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

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

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

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

C # Heap (ing) Против стека в .NET

Стек против кучи: узнайте разницу

Распределение памяти статического класса, где он хранится C #

Что и где находятся стек и куча?

https://en.wikipedia.org/wiki/Memory_management

https://en.wikipedia.org/wiki/Stack_register

Ресурсы на ассемблере:

Руководство по программированию на сборке

Разработчик программного обеспечения для архитектур Intel® 64 и IA-32 Руководства

person Olivier Rogier    schedule 05.07.2020

Спасибо за действительно хорошее обсуждение, но как настоящий новичок мне интересно, где хранятся инструкции? В НАЧАЛЕ ученые выбирали между двумя архитектурами (фон NEUMANN, где все считается ДАННЫМИ, и HARVARD, где одна область памяти была зарезервирована для инструкций, а другая - для данных). В конечном итоге мы выбрали дизайн фон Неймана, и теперь все считается «одинаковым». Это усложняло мне задачу, когда я изучал ассемблер https://www.cs.virginia.edu/~evans/cs216/guides/x86.html, потому что они говорят о регистрах и указателях стека.

Все выше говорит о ДАННЫХ. Я предполагаю, что, поскольку инструкция - это определенная вещь с определенным объемом памяти, она будет помещена в стек, и поэтому все «эти» регистры, обсуждаемые в сборке, находятся в стеке. Конечно, затем пришло объектно-ориентированное программирование с инструкциями и данными, объединенными в структуру, которая была динамической, поэтому теперь инструкции также будут храниться в куче?

person aquagremlin    schedule 09.04.2020
comment
AFAIK, до сих пор существует множество процессоров с гарвардской архитектурой (обычно оптимизированные DSP), которые имеют разделенную память (и шину) для инструкций и данных. (Некоторые из них даже более 1 памяти данных). Это сделано для оптимизации продолжительности цикла (выборка инструкций, выборка данных и выполнение предыдущей инструкции - все в одном машинном цикле), таким образом, используя преимущества того, что они называют конвейерной связью. Я не знаю, что инструкции должны каким-либо образом помещаться в стек. В любом случае, у меня есть подлое предчувствие, что этот ответ может немного выйти за рамки исходного вопроса ... - person Sold Out; 02.07.2020