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

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

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

 

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

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

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

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

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

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

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

 .i1a { width: 336px; height: 280px; }
 @media(min-width: 768px) { .i1a { width: 728px; height: 90px; } }
 @media(min-width: 992px) { .i1a { width: 360px; height: 300px; } }
 @media(min-width: 1083px) { .i1a { width: 728px; height: 90px; } }

 (adsbygoogle = window.adsbygoogle || []).push({});

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

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

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

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

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

Четкая демонстрация:
![](/img/79f90d3e297dcabbe8f74b64b0f43482.png)  
 Источник изображения:  [vikashazrati.wordpress.com](http://vikashazrati.wordpress.com/2007/10/01/quicktip-java-basics-stack-and-heap/)

 

  **Stack**

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

  **Heap:**

 .i2a { width: 336px; height: 280px; }
 @media(min-width: 768px) { .i2a { width: 728px; height: 90px; } }
 @media(min-width: 992px) { .i2a { width: 360px; height: 300px; } }
 @media(min-width: 1083px) { .i2a { width: 728px; height: 90px; } }

 (adsbygoogle = window.adsbygoogle || []).push({});

* Сохраняется в оперативной памяти компьютера так же, как и стек.
* В С++ переменные в куче должны быть уничтожены вручную и никогда не выпадают из области видимости. Данные освобождаются с помощью `delete`, `delete[]` или `free`.
* Медленнее выделять по сравнению с переменными в стеке.
* Используется по требованию для выделения блока данных для использования программой.
* Может иметь фрагментацию, когда есть много распределений и освобождений.
* В С++ или 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 a memory leak, I should have called delete[] pBuffer;

```
 

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

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

 ![Stack like a stack of papers](/img/d2a5aa63fed29d0158397c6a40812fce.jpg)

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

 ![Heap like a heap of licorice allsorts](/img/ac586078fa1d19141151ccf879d07f8b.jpg)

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

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

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

Как уже упоминалось, куча и стек являются общими терминами и могут быть реализованы разными способами. Компьютерные программы обычно имеют стек, называемый [стек вызовов](http://en.wikipedia.org/wiki/Call_stack), который хранит информацию, относящуюся к текущей функции, такую как указатель на какую бы функцию она не вызывалась, и любые локальные переменные. Поскольку функции вызывают другие функции, а затем возвращаются, стек растет и сжимается, чтобы удерживать информацию от функций дальше по стеку вызовов. У программы на самом деле нет контроля над ней; он определяется языком программирования, ОС и даже системной архитектурой.

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

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

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

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

Стек быстрее, потому что вся свободная память всегда смежна. Не нужно сохранять список всех сегментов свободной памяти, а всего лишь один указатель на текущую вершину стека. Компиляторы обычно хранят этот указатель в специальной, быстрой [register](http://en.wikipedia.org/wiki/Stack_register) для этой цели. Более того, последующие операции над стеком обычно сосредоточены в очень близких областях памяти, которые на очень низком уровне хороши для оптимизации кэшами на процессоре.

 

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

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

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

 Куча
-----

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

 ![The heap](/img/7835168e4d0405b6f0ec32742c901e00.png)

 Стек
-----

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

 ![The stack](/img/09329baa62d386e4a5cd9636af471f21.png)

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

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

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

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

 

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

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

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

  ![Picture of variables on the stack](/img/8d8d270a84fadd3444ef16190d205486.jpg)

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

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

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

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

  [Разница между распределением памяти стек и кучи "timmurphy.org](http://timmurphy.org/2010/08/11/the-difference-between-stack-and-heap-memory-allocation)

  и здесь:

  [Создание объектов в стеке и куче](https://root.cern.ch/root/htmldoc/guides/users-guide/ALittleC++.html#creating-objects-on-the-stack-and-heap%23creating-objects-on-the-stack-and-heap)

  Эта статья является источником изображения выше: [шесть важных понятий.NET: стек, куча, типы значений, ссылочные типы, бокс и распаковка - CodeProject](http://www.codeproject.com/Articles/76153/Six-important-NET-concepts-Stack-heap-value-types#Stack%20and%20Heap%23Stack)

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

 

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

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

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

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

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

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

  **Физическое расположение в памяти**
Это менее актуально, чем вы думаете из-за технологии [Virtual Memory](http://en.wikipedia.org/wiki/Virtual_memory), которая заставляет вашу программу думать, что у вас есть доступ к определенному адресу, где физические данные где-то еще (даже на жестком диске!). Адреса, которые вы получаете для стека, растут по мере того, как ваше дерево вызовов становится глубже. Адреса для кучи являются непредсказуемыми (например, специфическими) и, откровенно говоря, не важными.

 

Чтобы уточнить, [этот ответ](/questions/16/what-and-where-are-the-stack-and-heap/509#509) содержит неверную информацию ([thomas](/questions/16/what-and-where-are-the-stack-and-heap/504#504) зафиксировал свой ответ после комментариев, круто:)). Другие ответы просто не позволяют объяснить, что означает статическое распределение. Поэтому я объясню три основные формы распределения и то, как они обычно относятся к сегменту кучи, стека и данных ниже. Я также покажу несколько примеров как на C/С++, так и на Python, чтобы помочь людям понять.

 "Static" (AKA статически выделенные) переменные не выделяются в стеке. Не думайте так - многие люди делают это только потому, что "статические" звучат так же, как "стек". Они фактически не существуют ни в стеке, ни в куче. Это часть того, что называется сегментом данных [.](http://en.wikipedia.org/wiki/Data_segment)

Однако обычно лучше рассмотреть " **область**" и " **продолжительность жизни**", а не "стек" и "куча".

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

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

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

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

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

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

int main() {return 0;}

```

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

Некоторые люди думают об этих понятиях как о 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 " + 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 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it 13:05:02.256000, you should feed me. My favorite food is milkbones

```
 

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

* Стек и куча не обязательно должны быть сингулярными. Обычная ситуация, когда у вас более одного стека, есть несколько потоков в процессе. В этом случае каждый поток имеет свой собственный стек. Вы также можете иметь более одной кучи, например, некоторые конфигурации DLL могут приводить к разнесению разных DLL файлов из разных кучек, поэтому, как правило, это плохая идея освободить память, выделенную другой библиотекой.
* В C вы можете получить преимущество распределения переменной длины с помощью [alloca](https://www.freebsd.org/cgi/man.cgi?alloca), который выделяется в стеке, в отличие от alloc, который выделяется в куче. Эта память не сможет выдержать ваш оператор return, но он полезен для буфера с нуля.
* Создание огромного временного буфера в Windows, в котором вы не используете большую часть, не является бесплатным. Это связано с тем, что компилятор будет генерировать петлю зонда стека, которая вызывается каждый раз, когда будет введена ваша функция, чтобы убедиться, что стек существует (поскольку Windows использует одну страницу защиты в конце вашего стека, чтобы определить, когда нужно наращивать стек. Если вы получите доступ к памяти более одной страницы с конца стека, вы сработаете). Пример:

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

```
 

Другие ответили на ваш вопрос напрямую, но, пытаясь понять стек и кучу, я думаю, что полезно рассмотреть макет памяти традиционного процесса UNIX (без потоков и `mmap()` -распределителей). На веб-странице  ["Глоссарий управления памятью" есть схема этого макета памяти.](http://www.memorymanagement.org/glossary/b.html#term-brk)

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

В системах без виртуальной памяти, таких как некоторые встроенные системы, часто применяется тот же базовый макет, за исключением того, что стек и куча фиксированы по размеру. Однако в других встроенных системах (например, на микроконтроллерах Microchip PIC) программный стек представляет собой отдельный блок памяти, который не адресуется инструкциями по перемещению данных и может быть изменен или изменен только с помощью инструкций потока программы (вызов, возврат и т.д.). Другие архитектуры, такие как процессоры Intel Itanium, имеют [несколько стеков](http://blogs.msdn.com/oldnewthing/archive/2005/04/21/410397.aspx). В этом смысле стек является элементом архитектуры ЦП.

 

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

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

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

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

 

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

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

 

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

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

 [![Введите описание изображения здесь](/img/446028a7f70c95fcb67aff130d9e6825.jpg)](/img/446028a7f70c95fcb67aff130d9e6825.jpg)

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

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

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

 [![Введите описание изображения здесь](/img/73af35d97be3171cf6e377f5b8214336.jpg)](/img/73af35d97be3171cf6e377f5b8214336.jpg)

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

  **Оба вместе**

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

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

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

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

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

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

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

 [![Введите описание изображения здесь](/img/8e9a843adaea7cf1674ca43e3334bf95.png)](/img/8e9a843adaea7cf1674ca43e3334bf95.png)

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

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

 

Вы можете сделать некоторые интересные вещи со стеком. Например, у вас есть функции, такие как [alloca](http://everything2.com/title/alloca%2528%2529) (предполагая, что вы можете пройти мимо обильных предупреждений относительно его использования), которая является формой malloc, которая специально использует стек, а не кучу, для памяти.

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

 

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

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

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

 

Из WikiAnwser.

###  Stack

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

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

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

###  Heap

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

 

  **Stack**

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

  **Heap**

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

 

 **Хорошо,** просто и вкратце, они означают **упорядоченные** и **не упорядоченные**...!

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

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

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

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

  [![enter image description here](/img/9f5fb175f01315cb581e9de4c5872fb5.png)](/img/9f5fb175f01315cb581e9de4c5872fb5.png)

 

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

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

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

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

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

 ![A typical 1980s style UNIX C program memory layout](/img/1a2b3bb11aca123d0a52a441449bc383.png)

 

 Короче
-------

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

 

---

 В деталях
----------

  **Стек**

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

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

  Больше можно найти  **[здесь](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))**.

 

---

 **Куча**

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

  Если вы этого не сделаете, ваша программа будет иметь то, что известно как утечка памяти. То есть память в куче все равно будет выделена (и не будет доступна другим процессам). Как мы увидим в разделе отладки, есть инструмент под названием [Valgrind,](http://en.wikipedia.org/wiki/Valgrind) который может помочь вам обнаружить утечки памяти.

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

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

  Больше можно найти  **[здесь](https://en.wikipedia.org/wiki/Memory_management)**.

 

---

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

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

  [![Enter image description here](/img/708ffda44282bb7d19796e618b81626b.png)](/img/708ffda44282bb7d19796e618b81626b.png)

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

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

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

  [![Enter image description here](/img/b5a86fd69251a8d8238ddcf97fb80576.gif)](/img/b5a86fd69251a8d8238ddcf97fb80576.gif)

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

  Даже более подробно  [**здесь**](http://net-informations.com/faq/net/stack-heap.htm) и  [**здесь**](http://gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html).

 

---

 Теперь приходите к **вашему вопросу ответов**.

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

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

  Больше можно найти  **[здесь](/questions/16/what-and-where-are-the-stack-and-heap)**.

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

  Уже дано в топе.

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

  Больше можно найти [здесь](/questions/240/stack-static-and-heap-in-c).

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

  Размер стека устанавливается [ОС](https://en.wikipedia.org/wiki/Operating_system) при создании потока. Размер кучи устанавливается при запуске приложения, но может увеличиваться по мере необходимости в пространстве (распределитель запрашивает больше памяти у операционной системы).

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

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

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

  Подробности можно найти  **[здесь](/questions/241/which-is-faster-stack-allocation-or-heap-allocation)**.

 

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

  [![stack, heap and static data](https://bayanbox.ir/view/581244719208138556/virtual-memory.jpg)](https://bayanbox.ir/view/581244719208138556/virtual-memory.jpg)

 

* **Введение**

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

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

  **Рисунок 4-1.** Запоминание памяти CLR

  ![Enter image description here](/img/319d162f4ae46e6a3b07ecbe876eea2b.png)

  CLR обеспечивает абстрактный уровень управления памятью для виртуальной среды выполнения, используя службы оперативной памяти. Абстрактные понятия, используемые CLR, - это AppDomain, thread, stack, heapmemorymapped file и т.д. Концепция домена приложения (AppDomain) дает вашему приложению изолированную среду исполнения.

 * **Взаимодействие с памятью между CLR и ОС**

 Просмотрев трассировку стека при отладке следующего приложения С#, **используя WinDbg, вы увидите, как CLR использует базовые службы управления памятью ОС (например, метод HeapFree из KERNEL32.dll, метод RtlpFreeHeap из файла ntdll.dll)** для реализации его собственная модель памяти:

 
```
using System;
namespace CH_04
{
    class Program
    {
        static void Main(string[] args)
        {
            Book book = new Book();
            Console.ReadLine();
        }
    }

    public class Book
    {
        public void Print() { Console.WriteLine(ToString()); }
    }
}

```
  Скомпилированная сборка программы загружается в WinDbg для начала отладки. Для инициализации сеанса отладки используются следующие команды:

 
>   0: 000> sxe ld clrjit
> 
>   0: 000> г
> 
>   0: 000>.loadby sos clr
> 
>   0: 000>.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll
> 
>   Затем вы устанавливаете точку останова в основном методе класса Program, используя команду! Bpmd:
> 
>   0: 000>! Bpmd CH\_04.exe CH\_04.Program.Main
> 
>   Чтобы продолжить выполнение и перерыв в точке останова, выполните команду g:
> 
>   0: 000> г
> 
> 

  Когда выполнение прерывается в точке останова, вы используете команду! Eestack для просмотра сведений о трассировке стека всех потоков, выполняемых для текущего процесса. Следующий вывод показывает трассировку стека для всех потоков, запущенных для приложения CH\_04.exe:

 
>   0: 000>! Eestack
> 
>  
> 
> ---
> 
>  Ветвь дискуссии
> 
>   Текущий кадр: (MethodDesc 00233800 +0 CH\_04.Program.Main(System.String []))
> 
>   ChildEBP RetAddr Caller, Callee
> 
>   0022ed24 5faf21db clr! CallDescrWorker +0 x33
> 
>  /trace удален /
> 
>   0022f218 77712d68 ntdll! RtlFreeHeap +0 x142, вызывающий ntdll! RtlpFreeHeap
> 
>   0022f238 771df1ac KERNEL32! HeapFree +0 x14, вызывающий ntdll! RtlFreeHeap
> 
>   0022f24c 5fb4c036 clr! EEHeapFree +0 x36, вызывающий KERNEL32! HeapFree
> 
>   0022f260 5fb4c09d clr! EEHeapFreeInProcessHeap +0 x24, вызывающий clr! EEHeapFree
> 
>   0022f274 5fb4c06d clr! Operator delete [] +0 x30, вызывающий clr! EEHeapFreeInProcessHeap/trace удален /
> 
>   0022f4d0 7771316f ntdll! RtlpFreeHeap +0 xb7a, вызывающий ntdll! \_SEH\_epilog4
> 
>   0022f4d4 77712d68 ntdll! RtlFreeHeap +0 x142, вызывающий ntdll! RtlpFreeHeap
> 
>   0022f4f4 771df1ac KERNEL32! HeapFree +0 x14, вызывающий ntdll! RtlFreeHeap
> 
>  /trace удален /
> 
> 

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

  На рисунке 4-2 показана типичная модель памяти приложения С#, используемая CLR во время выполнения.

  **Рисунок 4-2**. Типичная модель памяти приложения С# ![enter image description here](/img/40f8670d0807e378ef3ef3e52fc3a268.png)

  **Модель памяти CLR тесно связана с службами управления памятью ОС.** **Чтобы понять модель памяти CLR, важно понять базовую модель памяти ОС.** Также важно знать, как адресное пространство физической памяти абстрагируется в адресное пространство виртуальной памяти, способы использования виртуального адресного пространства пользовательским приложением и системным приложением, как работает сопоставление виртуальных адресов, как память -mapped файл работает и т.д. Это знание поможет улучшить понимание концепций модели памяти CLR, включая AppDomain, стек и кучу.

  Для получения дополнительной информации см. Эту книгу:

   **[С# Deconstructed: Узнайте, как работает С# в.NET Framework](http://rads.stackoverflow.com/amzn/click/1430266708)**

  **Эта книга + ClrViaС# + Windows Internals** - отличные ресурсы для известной структуры.net в глубину и отношения с ОС.

 

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

  [![This is my vision of process memory construction with simplification for more easy understanding wht happening](/img/0d8510a685e0ab8affa163210dbfeef6.jpg)](/img/0d8510a685e0ab8affa163210dbfeef6.jpg)

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

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

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

 

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

Удивительно, но никто не упомянул, что множественные (т.е. не связанные с количеством запущенных потоков уровня ОС Windows) встречаются не только на экзотических языках (PostScript) или платформах (Intel Itanium), но и в [волокна](http://en.wikipedia.org/wiki/Fiber_(computer_science)), [зеленые темы](http://en.wikipedia.org/wiki/Green_threads) и некоторые реализации [сопрограммы](http://en.wikipedia.org/wiki/Coroutine).

Волокна, зеленые нити и сопрограммы во многом похожи, что приводит к большой путанице. Разница между волокнами и зеленой нитью заключается в том, что первые используют совместную многозадачность, в то время как последняя может иметь либо кооперативную, либо превентивную (или даже обе). Для различия между волокнами и сопрограммами см. [здесь](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4024.pdf).

В любом случае цель обоих волокон, зеленых нитей и сопрограмм имеет параллельное выполнение нескольких функций, но  **не** параллельно (см. [этот вопрос SO](/questions/237/concurrency-vs-parallelism-what-is-the-difference) для разграничения) в одном потоке уровня ОС, передавая управление назад и вперед друг от друга организованным образом.

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

Обратите внимание, что я сказал: "Обычно есть отдельный стек для каждой функции". Существуют как стековые, так и стековые реализации курок. Наиболее заметными стековыми реализациями С++ являются [Boost.Coroutine](http://www.boost.org/doc/libs/1_53_0/libs/coroutine/doc/html/index.html) и [Microsoft PPL](https://msdn.microsoft.com/en-us/library/dd492418.aspx) `async/await`. (Тем не менее, С++ [возобновляемые функции](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4134.pdf) (ака "`async` и `await`" ), которые были предложены С++ 17, скорее всего для использования стекированных сопрограмм.)

Предложено предложение волокон в стандартной библиотеке С++. Кроме того, есть сторонние [библиотеки](/questions/239/lightweight-portable-c-fibers-mit-license). Зеленые темы чрезвычайно популярны в таких языках, как Python и Ruby.

 

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

  **стек**

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

 **отвал**

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

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

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

 

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

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

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

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

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

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

 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  REF:

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

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

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

 

 стратегии выделения памяти по языкам программирования

  ПАМЯТЬ В С - СТЕК, ГОРКА И СТАТИЧЕСКИЙ

  [![enter image description here](/img/a7ef925dd835e3a6deea5b2ccbffc6e0.jpg)](/img/a7ef925dd835e3a6deea5b2ccbffc6e0.jpg) [![MEMORY IN C – THE STACK, THE HEAP, AND STATIC](/img/d70262ce88da9d6aa4c66e702be3caf2.jpg)](/img/d70262ce88da9d6aa4c66e702be3caf2.jpg)

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

 Я хочу добавить несколько слов об этом важном вопросе:

  ОС и общеязыковая среда выполнения

  Ключевые компоненты .NET Framework [![enter image description here](/img/038f9ae3234aa6f7101173ae30aff1ba.jpg)](/img/038f9ae3234aa6f7101173ae30aff1ba.jpg) Архитектура Net Framework 4.5 [![enter image description here](/img/c40ea2e3923b715eaaf3d6f0bcc6d772.png)](/img/c40ea2e3923b715eaaf3d6f0bcc6d772.png)

  Компоненты CLR [![Components of CLR](/img/dbcbb0d03fc3e473f02879355cf56462.gif)](/img/dbcbb0d03fc3e473f02879355cf56462.gif) [![enter image description here](/img/08019429b7766abc69718a83b40943f2.jpg)](/img/08019429b7766abc69718a83b40943f2.jpg) [![enter image description here](/img/176884f3aba1389fde7329745f0f82d2.jpg)](/img/176884f3aba1389fde7329745f0f82d2.jpg) [![enter image description here](/img/45b69d3c3b4388cbef7e9003e75c6933.jpg)](/img/45b69d3c3b4388cbef7e9003e75c6933.jpg)

 

 **Различия между стеком и кучей.**

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

  **Что такое стек:**

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

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

  **Что такое куча:**

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

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