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


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

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

7039


источник


Ответы:


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

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

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

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

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

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

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

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

Что определяет размер каждого из них?

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

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

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

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


5176



стек:

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

Heap:

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

Пример:

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

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

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

2073



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

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

    Stack like a stack of papers

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

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

    Heap like a heap of licorice allsorts

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

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

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

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

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

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

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

  • Что определяет размер каждого из них?

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

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

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

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


1251



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

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

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

Куча

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

The heap

Стек

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

The stack

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

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

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

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


659



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

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

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

Picture of variables on the stack

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

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

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

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

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

и здесь:

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

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

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


343



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

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

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

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

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

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

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


188



To clarify, this answer has incorrect information (thomas fixed his answer after comments, cool :) ). Other answers just avoid explaining what static allocation means. So I will explain the three main forms of allocation and how they usually relate to the heap, stack, and data segment below. I also will show some examples in both C/C++ and Python to help people understand.

"Static" (AKA statically allocated) variables are not allocated on the stack. Do not assume so - many people do only because "static" sounds a lot like "stack". They actually exist in neither the stack nor the heap. The are part of what's called the data segment.

However, it is generally better to consider "scope" and "lifetime" rather than "stack" and "heap".

Scope refers to what parts of the code can access a variable. Generally we think of local scope (can only be accessed by the current function) versus global scope (can be accessed anywhere) although scope can get much more complex.

Lifetime refers to when a variable is allocated and deallocated during program execution. Usually we think of static allocation (variable will persist through the entire duration of the program, making it useful for storing the same information across several function calls) versus automatic allocation (variable only persists during a single call to a function, making it useful for storing information that is only used during your function and can be discarded once you are done) versus dynamic allocation (variables whose duration is defined at runtime, instead of compile time like static or automatic).

Although most compilers and interpreters implement this behavior similarly in terms of using stacks, heaps, etc, a compiler may sometimes break these conventions if it wants as long as behavior is correct. For instance, due to optimization a local variable may only exist in a register or be removed entirely, even though most local variables exist in the stack. As has been pointed out in a few comments, you are free to implement a compiler that doesn't even use a stack or a heap, but instead some other storage mechanisms (rarely done, since stacks and heaps are great for this).

I will provide some simple annotated C code to illustrate all of this. The best way to learn is to run a program under a debugger and watch the behavior. If you prefer to read python, skip to the end of the answer :)

// 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.

A particularly poignant example of why it's important to distinguish between lifetime and scope is that a variable can have local scope but static lifetime - for instance, "someLocalStaticVariable" in the code sample above. Such variables can make our common but informal naming habits very confusing. For instance when we say "local" we usually mean "locally scoped automatically allocated variable" and when we say global we usually mean "globally scoped statically allocated variable". Unfortunately when it comes to things like "file scoped statically allocated variables" many people just say... "huh???".

Some of the syntax choices in C/C++ exacerbate this problem - for instance many people think global variables are not "static" because of the syntax shown below.

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

int main() {return 0;}

Note that putting the keyword "static" in the declaration above prevents var2 from having global scope. Nevertheless, the global var1 has static allocation. This is not intuitive! For this reason, I try to never use the word "static" when describing scope, and instead say something like "file" or "file limited" scope. However many people use the phrase "static" or "static scope" to describe a variable that can only be accessed from one code file. In the context of lifetime, "static" always means the variable is allocated at program start and deallocated when program exits.

Some people think of these concepts as C/C++ specific. They are not. For instance, the Python sample below illustrates all three types of allocation (there are some subtle differences possible in interpreted languages that I won't get into here).

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

166



Others have answered the broad strokes pretty well, so I'll throw in a few details.

  1. Stack and heap need not be singular. A common situation in which you have more than one stack is if you have more than one thread in a process. In this case each thread has its own stack. You can also have more than one heap, for example some DLL configurations can result in different DLLs allocating from different heaps, which is why it's generally a bad idea to release memory allocated by a different library.

  2. In C you can get the benefit of variable length allocation through the use of alloca, which allocates on the stack, as opposed to alloc, which allocates on the heap. This memory won't survive your return statement, but it's useful for a scratch buffer.

  3. Making a huge temporary buffer on Windows that you don't use much of is not free. This is because the compiler will generate a stack probe loop that is called every time your function is entered to make sure the stack exists (because Windows uses a single guard page at the end of your stack to detect when it needs to grow the stack. If you access memory more than one page off the end of the stack you will crash). Example:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

154



Others have directly answered your question, but when trying to understand the stack and the heap, I think it is helpful to consider the memory layout of a traditional UNIX process (without threads and mmap()-based allocators). The Memory Management Glossary web page has a diagram of this memory layout.

The stack and heap are traditionally located at opposite ends of the process's virtual address space. The stack grows automatically when accessed, up to a size set by the kernel (which can be adjusted with setrlimit(RLIMIT_STACK, ...)). The heap grows when the memory allocator invokes the brk() or sbrk() system call, mapping more pages of physical memory into the process's virtual address space.

In systems without virtual memory, such as some embedded systems, the same basic layout often applies, except the stack and heap are fixed in size. However, in other embedded systems (such as those based on Microchip PIC microcontrollers), the program stack is a separate block of memory that is not addressable by data movement instructions, and can only be modified or read indirectly through program flow instructions (call, return, etc.). Other architectures, such as Intel Itanium processors, have multiple stacks. In this sense, the stack is an element of the CPU architecture.


125



I think many other people have given you mostly correct answers on this matter.

One detail that has been missed, however, is that the "heap" should in fact probably be called the "free store". The reason for this distinction is that the original free store was implemented with a data structure known as a "binomial heap." For that reason, allocating from early implementations of malloc()/free() was allocation from a heap. However, in this modern day, most free stores are implemented with very elaborate data structures that are not binomial heaps.


106



The stack is a portion of memory that can be manipulated via several key assembly language instructions, such as 'pop' (remove and return a value from the stack) and 'push' (push a value to the stack), but also call (call a subroutine - this pushes the address to return to the stack) and return (return from a subroutine - this pops the address off of the stack and jumps to it). It's the region of memory below the stack pointer register, which can be set as needed. The stack is also used for passing arguments to subroutines, and also for preserving the values in registers before calling subroutines.

The heap is a portion of memory that is given to an application by the operating system, typically through a syscall like malloc. On modern OSes this memory is a set of pages that only the calling process has access to.

The size of the stack is determined at runtime, and generally does not grow after the program launches. In a C program, the stack needs to be large enough to hold every variable declared within each function. The heap will grow dynamically as needed, but the OS is ultimately making the call (it will often grow the heap by more than the value requested by malloc, so that at least some future mallocs won't need to go back to the kernel to get more memory. This behavior is often customizable)

Because you've allocated the stack before launching the program, you never need to malloc before you can use the stack, so that's a slight advantage there. In practice, it's very hard to predict what will be fast and what will be slow in modern operating systems that have virtual memory subsystems, because how the pages are implemented and where they are stored is an implementation detail.


105



What is a stack?

A stack is a pile of objects, typically one that is neatly arranged.

Enter image description here

Stacks in computing architectures are regions of memory where data is added or removed in a last-in-first-out manner.
In a multi-threaded application, each thread will have its own stack.

What is a heap?

A heap is an untidy collection of things piled up haphazardly.

Enter image description here

In computing architectures the heap is an area of dynamically-allocated memory that is managed automatically by the operating system or the memory manager library.
Memory on the heap is allocated, deallocated, and resized regularly during program execution, and this can lead to a problem called fragmentation.
Fragmentation occurs when memory objects are allocated with small spaces in between that are too small to hold additional memory objects.
The net result is a percentage of the heap space that is not usable for further memory allocations.

Both together

In a multi-threaded application, each thread will have its own stack. But, all the different threads will share the heap.
Because the different threads share the heap in a multi-threaded application, this also means that there has to be some coordination between the threads so that they don’t try to access and manipulate the same piece(s) of memory in the heap at the same time.

Which is faster – the stack or the heap? And why?

The stack is much faster than the heap.
This is because of the way that memory is allocated on the stack.
Allocating memory on the stack is as simple as moving the stack pointer up.

For people new to programming, it’s probably a good idea to use the stack since it’s easier.
Because the stack is small, you would want to use it when you know exactly how much memory you will need for your data, or if you know the size of your data is very small.
It’s better to use the heap when you know that you will need a lot of memory for your data, or you just are not sure how much memory you will need (like with a dynamic array).

Java Memory Model

Enter image description here

The stack is the area of memory where local variables (including method parameters) are stored. When it comes to object variables, these are merely references (pointers) to the actual objects on the heap.
Every time an object is instantiated, a chunk of heap memory is set aside to hold the data (state) of that object. Since objects can contain other objects, some of this data can in fact hold references to those nested objects.


94