Книги по языку программирования объясняют, что типы значений создаются в куча, а ссылочные типы создаются в куча, без объяснения, что это за две вещи. Я не читал четкого объяснения этого. Я понимаю, что такое стек. Но,
Также (действительно) хорошо: codeproject.com/Articles/76153/… (часть стека / кучи)
Связанные, см. Столкновение стека. Исправления Stack Clash затронули некоторые аспекты системных переменных и поведения, например rlimit_stack
. Также см. Red Hat Проблема 1463241
@mattshane Определения стека и кучи вообще не зависят от типов значений и ссылок. Другими словами, стек и куча могут быть полностью определены, даже если типы значений и ссылочные типы никогда не существовали. Кроме того, при понимании типов значений и ссылок стек - это просто деталь реализации. Согласно Эрику Липперту: Стек - это деталь реализации, часть первая.
Непонятно в ответах: для языковой среды выполнения (например, .NET) существует один стек на поток для управления вызовами методов / локальными переменными и только одна куча, установленная для всех процессов среды выполнения. Куча контролируется сборщиком мусора. Стеки времени выполнения / пространство кучи - это часть непрерывной виртуальной памяти, выделяемой ОС для процессов (которая сама загружается физическими блоками ОЗУ в произвольном порядке) по запросу процессов. Путаница вокруг «стеков» связана с существованием в компьютере множества видов «стеков», не связанных со стеками языковой среды выполнения. «Стек» - это просто структура хранения LIFO.
Если вы хотите увидеть симуляцию того, как выглядят стек и куча во время выполнения программы на C, попробуйте C Репетитор.
Куча:
Куча:
delete
, delete[]
или free
.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;
Указатель pBuffer и значение b находятся в стеке и, скорее всего, выделяются на входе в функцию. В зависимости от компилятора буфер также может быть выделен при входе в функцию.
Распространенное заблуждение, что язык C
, как определено языковым стандартом C99
(доступным по адресу open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf), требует «стека». Фактически, слово «стек» даже не встречается в стандарте. Это отвечает на утверждения относительно использования стека C
в целом, но это никоим образом не требуется для языка. См. knosof.co.uk/cbook/cbook.html для получения дополнительной информации и, в частности, о том, как C
реализован на необычных архитектурах, таких как en.wikipedia.org/wiki/Burroughs_large_systems
@Brian Вы должны объяснить, что Почему buffer [] и указатель pBuffer создаются в стеке и почему данные pBuffer создаются в куче. Я думаю, что некоторые люди могут быть сбиты с толку вашим ответом, поскольку они могут подумать, что программа специально указывает, что память выделяется в стеке, а не в куче, но это не так. Это потому, что Buffer - это тип значения, тогда как pBuffer - ссылочный тип?
@Hershi: Re 1. Это уже покрыто: переменные, созданные в стеке, выйдут из области видимости и автоматически освободятся. По поводу 2. Это уже рассмотрено: Переменные в куче должны быть уничтожены вручную и никогда не выпадают из области видимости. Данные освобождаются с помощью delete, delete [] или free
@Brian Я новичок, и у меня есть вопрос по этой теме. Если я определенным образом объявляю переменную, находится ли она в стеке или в куче? Нет другого места, где находится объявленная переменная? А как насчет статических членов?
@ BrianR.Bondy, не могли бы вы объяснить, почему медленнее размещать переменную в куче ?? по сравнению со стеком.
Я думаю, потому что выделение чего-либо в стеке просто увеличивает верхнюю часть указателя стека. Для кучи нужно найти доступное место достаточно большого размера. Не совсем уверен, но я знаю, что стек намного, намного быстрее.
@ BrianR.Bondy, не могли бы вы объяснить, что на самом деле вызывает размещение переменной в куче. Кажется неявным, что переменные, объявленные как указатели, размещаются в куче, иначе - в стеке. Но всегда ли это так?
@Remover: указатель не содержит адреса и может указывать на что-то в куче или стеке в равной степени. new, malloc и некоторые другие функции, похожие на malloc, выделяют в куче и возвращают адрес выделенной памяти. Зачем вам выделять память в куче? Так что ваша память не выйдет за рамки и не освободится, пока вы этого не захотите.
«Ответственный за утечки памяти» - Heaps не несут ответственности за утечки памяти! Ленивые / забывчивые / бывшие java-кодеры / кодеры, которым наплевать, - это!
Также неверны комментарии по поводу области видимости и распределения - область действия вообще не связана со стеком или кучей. Переменные в куче должны быть уничтожены вручную и никогда не выпадают из области видимости. неверно; правильнее было бы сказать: «Данные в куче не освобождаются, когда переменные, которые на них ссылаются, выходят за пределы области видимости. Вы (или сборщик мусора) должны освободить их.
Прочтите другой ответ «Компьютерная память - это просто набор адресов;« куча »и« стек »- изобретения компилятора». это правда?
@ BrianR.Bondy Привет, я сомневаюсь, что, если я выделил память в куче и не освободил ее. Так будет ли эта память освобождена после завершения программы?
Итак, стеки и кучи - это абстракции, очевидно, созданные с использованием реальных структур данных. Но вам не хватает одного момента: кто создает и отвечает за жизненный цикл стека и кучи? Это операционная система? И как именно создаются эти стек и куча? Если вы не хотите включать эту информацию непосредственно в свой ответ, не могли бы вы дать ссылку на книгу или источник, где это подробно объясняется? Кроме того, ваши перечисления могли бы быть лучше, а точки перечислений можно было бы упорядочить более логичным образом!
@RohitSaluja Большинство операционных систем освобождают неиспользованную память при завершении программы, но все же хороший способ - это явно освободить всю динамически выделяемую память.
Сборщик мусора НЕ управляет стеками. Поэтому любое выделение или освобождение памяти должно выполняться вручную.
Один незначительный / особый случай, регистры в процессоре - для компилятора C / C++, если обнаружено, что переменная "стек" имеет очень короткое время жизни или код может быть оптимизирован надлежащим образом, переменная может никогда не попасть в то, о чем может подумать пользователь в качестве «памяти», но В самом деле должен быть под рукой к процессору в его собственный небольшом количестве рабочих регистров. Это часто имеет место для значений, используемых для «индексации» в массиве вещей, потому что, особенно для процессоров со сложным набором инструкций, они имеют быстрые / эффективные операционные коды, которые использовать этих регистров для этого типа операции!
Стек Когда вы вызываете функцию, аргументы этой функции плюс некоторые другие накладные расходы помещаются в стек. Некоторая информация (например, куда пойти по возвращении) также хранится там. Когда вы объявляете переменную внутри своей функции, эта переменная также выделяется в стеке.
Освободить стек довольно просто, потому что вы всегда освобождаете в обратном порядке. Материал стека добавляется при вводе функций, соответствующие данные удаляются при выходе из них. Это означает, что вы склонны оставаться в небольшой области стека, если вы не вызываете множество функций, которые вызывают множество других функций (или не создаете рекурсивное решение).
Куча Куча - это общее имя, куда вы помещаете данные, которые вы создаете на лету. Если вы не знаете, сколько космических кораблей собирается создать ваша программа, вы, вероятно, воспользуетесь оператором new (или malloc или эквивалентным) для создания каждого космического корабля. Это распределение будет сохраняться какое-то время, поэтому вполне вероятно, что мы будем освобождать вещи в другом порядке, чем мы их создавали.
Таким образом, куча намного сложнее, потому что в конечном итоге остаются неиспользуемые области памяти, чередующиеся с фрагментами, которые - память фрагментируется. Найти свободную память нужного вам размера - сложная задача. Вот почему следует избегать кучи (хотя она все еще часто используется).
Выполнение Реализация как стека, так и кучи обычно зависит от среды выполнения / ОС. Часто игры и другие приложения, которые критичны к производительности, создают свои собственные решения для памяти, которые захватывают большой кусок памяти из кучи и затем распределяют его внутри, чтобы не полагаться на ОС в качестве памяти.
Это практично только в том случае, если ваше использование памяти сильно отличается от нормы - например, для игр, где вы загружаете уровень за одну огромную операцию и можете выбросить все это в другой огромной операции.
Физическое расположение в памяти Это менее актуально, чем вы думаете, из-за технологии под названием Виртуальная память, которая заставляет вашу программу думать, что у вас есть доступ к определенному адресу, где физические данные находятся где-то еще (даже на жестком диске!). Адреса, которые вы получаете для стека, располагаются в порядке возрастания по мере того, как ваше дерево вызовов становится глубже. Адреса кучи непредсказуемы (т.е. специфичны для реализации) и, откровенно говоря, не важны.
Рекомендация избегать использования кучи довольно сильна. Современные системы имеют хорошие менеджеры кучи, а современные динамические языки широко используют кучу (без особого беспокойства программиста об этом). Я бы сказал, используйте кучу, но с ручным распределителем не забудьте освободить!
Если вы можете использовать стек или кучу, используйте стек. Если вы не можете использовать стек, выбора действительно нет. Я часто использую и то, и другое, и, конечно же, std :: vector или аналогичный попадает в кучу. Новичку лучше избегать кучи, потому что стек - это просто!
Если ваш язык не реализует сборку мусора, интеллектуальные указатели (отдельные объекты, которые оборачиваются вокруг указателя, который выполняет подсчет ссылок для динамически выделяемых фрагментов памяти) тесно связаны со сборкой мусора и являются достойным способом управления кучей в безопасном режиме. и без утечек. Они реализованы в различных фреймворках, но их также не так сложно реализовать в ваших собственных программах.
«Вот почему следует избегать кучи (хотя она все еще часто используется)». Я не уверен, что это означает на практике, тем более что память на многих языках высокого уровня управляется по-разному. Поскольку этот вопрос помечен как независимый от языка, я бы сказал, что этот конкретный комментарий / строка неуместны и неприменимы.
Хороший замечание @JonnoHampson - хотя вы и говорите справедливо, я бы сказал, что если вы работаете на «языке высокого уровня» с GC, вам, вероятно, вообще не нужны механизмы распределения памяти - и поэтому не даже неважно, что такое стек и куча.
Стек - это часть памяти, которой можно управлять с помощью нескольких ключевых инструкций языка ассемблера, таких как pop (удаление и возврат значения из стека) и push (отправка значения в стек), но также вызов ( вызов подпрограммы - это подталкивает адрес к возврату в стек) и return (возврат из подпрограммы - это выталкивает адрес из стека и переходит к нему). Это область памяти ниже регистра указателя стека, которую можно настроить по мере необходимости. Стек также используется для передачи аргументов подпрограммам, а также для сохранения значений в регистрах перед вызовом подпрограмм.
Куча - это часть памяти, которая предоставляется приложению операционной системой, как правило, с помощью системного вызова, такого как malloc. В современных операционных системах эта память представляет собой набор страниц, к которым имеет доступ только вызывающий процесс.
Размер стека определяется во время выполнения и обычно не увеличивается после запуска программы. В программе на C стек должен быть достаточно большим, чтобы вместить каждую переменную, объявленную в каждой функции. Куча будет расти динамически по мере необходимости, но ОС в конечном итоге выполняет вызов (она часто увеличивает кучу больше, чем значение, запрошенное malloc, так что по крайней мере некоторым будущим маллокам не нужно будет возвращаться к ядру, чтобы получить больше памяти. Это поведение часто настраивается)
Поскольку вы выделили стек перед запуском программы, вам никогда не нужно выполнять malloc, прежде чем вы сможете использовать стек, так что это небольшое преимущество. На практике очень сложно предсказать, что будет быстрым, а что медленным в современных операционных системах с подсистемами виртуальной памяти, потому что то, как реализованы страницы и где они хранятся, является деталью реализации.
Также здесь стоит упомянуть, что Intel сильно оптимизирует доступ к стеку, особенно такие вещи, как предсказание того, куда вы вернетесь из функции.
Другие довольно хорошо ответили на общие черты, поэтому я добавлю несколько деталей.
Стек и куча не обязательно должны быть единичными. Распространенная ситуация, когда у вас есть более одного стека, - это если у вас более одного потока в процессе. В этом случае у каждого потока есть свой стек. У вас также может быть более одной кучи, например, некоторые конфигурации DLL могут приводить к тому, что разные библиотеки DLL выделяются из разных куч, поэтому обычно плохая идея освобождать память, выделенную другой библиотекой.
В C вы можете получить преимущество выделения переменной длины за счет использования alloca, который выделяется в стеке, в отличие от alloc, который выделяется в куче. Эта память не выдержит вашего оператора return, но она полезна для временного буфера.
Создание огромного временного буфера в Windows, который вы мало используете, не является бесплатным. Это связано с тем, что компилятор будет генерировать цикл проверки стека, который вызывается каждый раз при входе в вашу функцию, чтобы убедиться, что стек существует (потому что Windows использует одну страницу защиты в конце вашего стека, чтобы определить, когда ей нужно увеличить стек. Если вы обращаетесь к памяти более чем на одну страницу с конца стека, произойдет сбой). Пример:
void myfunction()
{
char big[10000000];
// Do something that only uses for first 1K of big 99% of the time.
}
Re "в отличие от alloc": Вы имеете в виду "в отличие от malloc"?
Насколько портативен alloca
?
@PeterMortensen, это не POSIX, переносимость не гарантируется.
Стек - это память, выделенная как временное пространство для потока выполнения. Когда функция вызывается, наверху стека резервируется блок для локальных переменных и некоторых данных бухгалтерского учета. Когда эта функция возвращается, блок становится неиспользуемым и может быть использован при следующем вызове функции. Стек всегда резервируется в порядке LIFO (последний пришел - первый ушел); самый последний зарезервированный блок всегда является следующим освобождаемым блоком. Это упрощает отслеживание стека; освобождение блока из стека - это не что иное, как настройка одного указателя.
Куча - это память, отведенная для динамического распределения. В отличие от стека, здесь нет принудительного шаблона для выделения и освобождения блоков из кучи; вы можете выделить блок в любое время и освободить его в любое время. Это значительно усложняет отслеживание того, какие части кучи выделены или освобождены в любой момент времени; доступно множество настраиваемых распределителей кучи для настройки производительности кучи для различных шаблонов использования.
Каждый поток получает стек, в то время как обычно существует только одна куча для приложения (хотя нередко бывает несколько куч для разных типов распределения).
Чтобы напрямую ответить на ваши вопросы:
To what extent are they controlled by the OS or language runtime?
ОС выделяет стек для каждого потока системного уровня при создании потока. Обычно ОС вызывается средой выполнения языка для выделения кучи для приложения.
What is their scope?
Стек прикреплен к потоку, поэтому, когда поток выходит из стека, он восстанавливается. Куча обычно выделяется при запуске приложения средой выполнения и освобождается при выходе из приложения (технически процесса).
What determines the size of each of them?
Размер стека устанавливается при создании потока. Размер кучи устанавливается при запуске приложения, но может увеличиваться по мере необходимости в пространстве (распределитель запрашивает у операционной системы больше памяти).
What makes one faster?
Стек работает быстрее, потому что шаблон доступа упрощает выделение и освобождение памяти из него (указатель / целое число просто увеличивается или уменьшается), в то время как в куче гораздо более сложный учет, связанный с выделением или освобождением. Кроме того, каждый байт в стеке имеет тенденцию очень часто повторно использоваться, что означает, что он имеет тенденцию отображаться в кеш-память процессора, что делает его очень быстрым. Еще одним ударом по производительности кучи является то, что куча, являющаяся в основном глобальным ресурсом, обычно должна быть многопоточной, то есть каждое выделение и освобождение должно быть - обычно - синхронизировано со «всеми» другими доступами к куче в программе.
Наглядная демонстрация:
Image source: vikashazrati.wordpress.com
Хороший ответ, но я думаю, вы должны добавить, что, хотя стек выделяется ОС при запуске процесса (при условии существования ОС), он поддерживается встроенной программой. Это еще одна причина, по которой стек работает быстрее - операции push и pop обычно представляют собой одну машинную инструкцию, и современные машины могут выполнять как минимум 3 из них за один цикл, тогда как выделение или освобождение кучи включает вызов кода ОС.
Меня очень смущает диаграмма в конце. Я думал, что понял, пока не увидел это изображение.
@Anarelle процессор выполняет инструкции с операционной системой или без нее. Мне очень близок пример SNES, в котором не было вызовов API, не было ОС в том виде, в каком мы ее знаем сегодня, но у нее был стек. Выделение в стеке - это сложение и вычитание в этих системах, и это нормально для переменных, уничтожаемых, когда они выталкиваются путем возврата из функции, которая их создала, но ограничьте это, скажем, конструктором, результат которого не может быть просто выброшенный. Для этого нам понадобится куча, которая не привязана к вызову и возврату. У большинства ОС есть куча API-интерфейсов, нет причин делать это самостоятельно
«стек - это память, отведенная под временное пространство». Круто. Но где это собственно «отложено» в терминах структуры памяти Java ?? Это память кучи / память без кучи / другое (структура памяти Java согласно betsol.com/2017/06/…)
@JatinShashoo Среда выполнения Java, как интерпретатор байт-кода, добавляет еще один уровень виртуализации, так что то, что вы упомянули, является всего лишь точкой зрения приложения Java. С точки зрения операционной системы, это всего лишь куча, в которой процесс времени выполнения Java выделяет часть своего пространства как «не-кучную» память для обрабатываемого байт-кода. Остальная часть этой кучи уровня ОС используется как куча уровня приложения, где хранятся данные объекта.
Итак, стек - это «отложенная» куча в терминах уровня ОС. Стек управляется потоком напрямую с помощью инструкций ЦП, а куча - это область обычной памяти, отображаемая ОС.
Здравствуй. Я думаю, что в вашем сообщении (что в остальном здорово) четко не указано, что куча медленнее, чем стек, только когда вы СОЗДАЕТЕ материал (объекты). Когда вы ДОСТУПНЫ к материалу, куча должна иметь примерно такую же скорость, как и стек.
@sqykly Было бы правильно называть старые игровые ОС сами по себе? У них есть своего рода графический интерфейс, они управляют низкоуровневым вводом-выводом и имеют полный доступ ко всему аппаратному обеспечению, а также работают как процесс «корневого уровня». Именно по этим причинам я всегда видел их такими, и даже на таких новых консолях, как Wii, на самом деле запускалось только одно: меню HOME было встроено в каждую игру, а не предоставлялось "основным" ОС »(главное меню), которая вообще не работает в фоновом режиме. За исключением новых консолей, большинство старых игр, кажется, исполняют роль ОС.
@SinaMadani, хахаха, и здесь.
Хороший ответ. Но вы должны сказать, что стек также быстрее, потому что он может храниться в регистре процессора, а не в памяти.
Я понял то, о чем вы говорите в первых двух абзацах, когда я изучил архитектуру MIPS и что на самом деле стек / куча (регистры) / как память распределяется в стеке и куча на низком уровне, то есть сборка / машина язык. Я думаю, что когда мы изучаем низкий уровень, мы получаем более точное определение «как» / «когда» что-то происходит с кучей и стеком.
Еще несколько примечаний, которые могут иметь отношение к читателям: 1. блок управления памятью (MMU) сопоставляет адреса физической памяти в таблице страниц с виртуальными адресами. 2. Стек полезен для поддержания непрерывной локальности памяти, где данные в куче могут быть распределены между несколькими устройствами памяти, такими как ОЗУ, кэш L1, кеш L2 и т. д. 3 рассмотрите возможность хранения фиксированного массива из 10 элементов, расположенных рядом друг с другом в памяти. в стеке итерация / поиск элемента тривиальны. 10 элементов в связанном списке в куче могут потребовать поиска не только в другом блоке памяти, но и в разных устройствах памяти.
@SinaMadani - Схема показывает, что локальные переменные и методы зарезервированы в стеке, но они ссылаются на объекты, размещенные в куче.
На диаграмме видно, что локальные переменные в стеке ссылаются только на объекты в куче. Возможно ли, чтобы локальные переменные также ссылались на объекты в стеке? Если я, например, выполняю int x = 5;
внутри вызова функции в C, разве это 5 не хранится в стеке?
Я думаю, что многие другие люди дали вам в основном правильные ответы по этому поводу.
Однако упущена одна деталь: "кучу" на самом деле, вероятно, следует называть "бесплатным хранилищем". Причина этого различия заключается в том, что исходное бесплатное хранилище было реализовано со структурой данных, известной как «биномиальная куча». По этой причине распределение из ранних реализаций malloc () / free () было распределением из кучи. Однако в наши дни большинство бесплатных хранилищ реализованы с очень сложными структурами данных, которые не являются биномиальными кучами.
Еще одна придирка - большинство ответов (слегка) подразумевают, что использование «стека» требуется языком C
. Это распространенное заблуждение, хотя это (безусловно) доминирующая парадигма для реализации C99 6.2.4 automatic storage duration objects
(переменных). Фактически, слово «стек» даже не встречается в языковом стандарте C99
: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
[@Heath] У меня есть небольшой комментарий к вашему ответу. Взгляните на принятый ответ на этот вопрос. В нем говорится, что бесплатный магазинскорее всего совпадает с куча, но не обязательно.
Другие прямо ответили на ваш вопрос, но, пытаясь понять стек и кучу, я думаю, полезно рассмотреть структуру памяти традиционного процесса UNIX (без потоков и распределителей на основе mmap()
). На веб-странице Глоссарий по управлению памятью есть диаграмма этой структуры памяти.
Стек и куча традиционно расположены на противоположных концах виртуального адресного пространства процесса. Стек автоматически увеличивается при доступе до размера, установленного ядром (который можно настроить с помощью setrlimit(RLIMIT_STACK, ...)
). Куча увеличивается, когда распределитель памяти вызывает системный вызов brk()
или sbrk()
, отображая больше страниц физической памяти в виртуальное адресное пространство процесса.
В системах без виртуальной памяти, таких как некоторые встроенные системы, часто применяется одна и та же базовая схема, за исключением того, что размер стека и кучи фиксирован. Однако в других встроенных системах (например, на базе микроконтроллеров Microchip PIC) программный стек представляет собой отдельный блок памяти, который не адресуется инструкциями перемещения данных и может быть изменен или прочитан только косвенно с помощью инструкций потока программы (call, возврат и т. д.). Другие архитектуры, такие как процессоры Intel Itanium, имеют несколько стеков. В этом смысле стек является элементом архитектуры ЦП.
Проще говоря, стек - это место, где создаются локальные переменные. Кроме того, каждый раз, когда вы вызываете подпрограмму, счетчик программы (указатель на следующую машинную инструкцию) и любые важные регистры, а иногда и параметры помещаются в стек. Затем любые локальные переменные внутри подпрограммы помещаются в стек (и используются оттуда). Когда подпрограмма завершается, весь этот материал удаляется из стека. Данные ПК и регистров возвращаются и возвращаются туда, где они были, так что ваша программа может продолжать свой веселый путь.
Куча - это область памяти, из которой выполняется распределение динамической памяти (явные вызовы «new» или «allocate»). Это специальная структура данных, которая может отслеживать блоки памяти различного размера и их статус распределения.
В «классических» системах ОЗУ располагалась так, что указатель стека начинался снизу памяти, указатель кучи начинался сверху, и они увеличивались по направлению друг к другу. Если они перекрываются, у вас закончилась оперативная память. Однако это не работает с современными многопоточными ОС. У каждого потока должен быть свой собственный стек, и они могут создаваться динамически.
[@ T.E.D.] Почему вы сказали «иногда параметры помещаются в стек»? Что я знаю, так это то, что они всегда. Не могли бы вы подробнее рассказать?
@OmarOthman - я говорю это, потому что это полностью зависит от автора вашего компилятора / интерпретатора, что происходит при вызове подпрограммы. Классическое поведение Fortran - вообще не использовать стек. Некоторые языки поддерживают такие экзотические вещи, как передача по имени, которая фактически является текстовой заменой.
Со стеком можно делать кое-что интересное. Например, у вас есть такие функции, как alloca (при условии, что вы можете обойти многочисленные предупреждения, касающиеся его использования), которая является формой malloc, которая специально использует стек, а не кучу, для памяти.
Тем не менее, стековые ошибки памяти - одни из худших, с которыми мне приходилось сталкиваться. Если вы используете память кучи и вы выходите за границы выделенного блока, у вас есть приличный шанс вызвать ошибку сегмента. (Не на 100%: ваш блок может случайно быть смежным с другим, который вы ранее разместили.) Но поскольку переменные, созданные в стеке, всегда смежны друг с другом, запись за пределы может изменить значение другой переменной. Я узнал, что всякий раз, когда я чувствую, что моя программа перестала подчиняться законам логики, это, вероятно, переполнение буфера.
Насколько портативен alloca
? Например, работает ли он в Windows? Только для Unix-подобных операционных систем?
Из WikiAnwser.
Когда функция или метод вызывает другую функцию, которая по очереди вызывает другую функцию и т. д., Выполнение всех этих функций остается приостановленным до тех пор, пока самая последняя функция не вернет свое значение.
Эта цепочка приостановленных вызовов функций является стеком, потому что элементы в стеке (вызовы функций) зависят друг от друга.
Стек важно учитывать при обработке исключений и выполнении потоков.
Куча - это просто память, используемая программами для хранения переменных. Элементы кучи (переменные) не зависят друг от друга и всегда могут быть доступны случайным образом в любое время.
В следующем коде C#
public void Method1()
{
int i = 4;
int y = 2;
class1 cls1 = new class1();
}
Вот как управляется память
Local Variables
, который должен длиться только до тех пор, пока вызов функции находится в стеке. Куча используется для переменных, время жизни которых мы не знаем заранее, но ожидаем, что они продлятся какое-то время. Для большинства языков очень важно знать во время компиляции, насколько велика переменная, если мы хотим сохранить ее в стеке.
Объекты (которые меняются по размеру по мере их обновления) отправляются в кучу, потому что во время создания мы не знаем, как долго они прослужат. Во многих языках куча собирает мусор для поиска объектов (таких как объект cls1), на которые больше нет ссылок.
В Java большинство объектов попадают прямо в кучу. В таких языках, как C / C++, структуры и классы часто могут оставаться в стеке, когда вы не имеете дело с указателями.
Более подробную информацию можно найти здесь:
Разница между выделением памяти в стеке и куче «timmurphy.org
и тут:
Создание объектов в стеке и куче
Эта статья является источником изображения выше: Шесть важных концепций .NET: стек, куча, типы значений, ссылочные типы, упаковка и распаковка - CodeProject
но имейте в виду, что он может содержать некоторые неточности.
Это неверно. i и cls не являются «статическими» переменными. они называются «локальными» или «автоматическими» переменными. Это очень важное различие. См. [Ссылка] stackoverflow.com/a/13326916/1763801 для уточнения
Я не сказал, что они статические переменные. Я сказал, что int и cls1 статические Предметы. Их память выделяется статически, и поэтому они отправляются в стек. В этом отличие от объекта, которому требуется динамическое выделение памяти, которое, следовательно, хранится в куче.
Цитирую «Статические предметы ... уходят в стопку». Это просто неправильно. Статические элементы помещаются в сегмент данных, автоматические элементы помещаются в стек.
Также тот, кто написал эту статью о кодпроекте, не знает, о чем он говорит. Например, он говорит, что «примитивным нужна память статического типа», что совершенно неверно. Ничто не мешает вам динамически размещать примитивы в куче, просто напишите что-нибудь вроде «int array [] = new int [num]» и вуаля, примитивы размещаются динамически в .NET. Это лишь одна из нескольких неточностей.
Если вы посмотрите мою репутацию, вы увидите, что я еще не могу проголосовать против. Так что это даже не я голосую против.
@Snow Crash, в вашем ответе в начале This helps:
ссылка битая. Пожалуйста, исправьте это. Спасибо.
Какой инструмент вы использовали, чтобы нарисовать это изображение?
Я отредактировал ваш пост, потому что вы допустили серьезные технические ошибки в отношении того, что находится в стеке и куче.
@SnowCrash один вопрос по поводу вашего изображения - как мне получить доступ к i
после выделения y
? Надо ли выскакивать y
? Поменять их местами? Что, если их разделяет много локальных переменных?
@ confused00 У вас еще есть ответ на этот вопрос? Я думаю, что все они хранятся в массиве, подобном структуре, и во время компиляции они помещаются в стек операндов, верно?
@ confused00 Ни то, ни другое - потому что это не стопки. Это фреймы стека вызовов. Каждый кадр можно просто катать как угодно. Тогда еще одно неточное / вводящее в заблуждение описание!
Чтобы уточнить, у этот ответ неверная информация (Томас исправил свой ответ после комментариев, круто :)). В других ответах просто не объясняется, что означает статическое распределение. Итак, я объясню три основных формы распределения и то, как они обычно связаны с кучей, стеком и сегментом данных ниже. Я также покажу несколько примеров как на C / C++, так и на Python, чтобы помочь людям понять.
«Статические» (также известные как статически размещенные) переменные не размещаются в стеке. Не думайте об этом - многие люди так делают только потому, что «статика» очень похожа на «стек». На самом деле они не существуют ни в стеке, ни в куче. Это часть того, что называется сегмент данных.
Однако обычно лучше рассматривать «объем» и «продолжительность жизни», а не «стек» и «кучу».
Область действия относится к тому, какие части кода могут обращаться к переменной. Обычно мы думаем о локальный охват (доступ к нему возможен только для текущей функции) по сравнению с глобальный масштаб (доступ к нему можно получить где угодно), хотя область видимости может стать намного более сложной.
Время жизни означает, когда переменная выделяется и освобождается во время выполнения программы. Обычно мы думаем о статическое распределение (переменная будет сохраняться на протяжении всей программы, что делает ее полезной для хранения одной и той же информации в нескольких вызовах функций) по сравнению с автоматическое распределение (переменная сохраняется только во время одного вызова функции, что делает ее полезной для хранения информации. который используется только во время вашей функции и может быть отброшен, как только вы закончите) по сравнению с динамическое размещение (переменные, продолжительность которых определяется во время выполнения, а не во время компиляции, например статическая или автоматическая).
Хотя большинство компиляторов и интерпретаторов реализуют это поведение аналогичным образом с точки зрения использования стеков, куч и т. д., Компилятор может иногда нарушать эти соглашения, если он хочет, если поведение является правильным. Например, из-за оптимизации локальная переменная может существовать только в регистре или полностью удаляться, даже если большинство локальных переменных существует в стеке. Как было указано в нескольких комментариях, вы можете реализовать компилятор, который даже не использует стек или кучу, а вместо этого использует некоторые другие механизмы хранения (редко, поскольку для этого отлично подходят стеки и кучи).
Я предоставлю простой аннотированный код на языке C, чтобы проиллюстрировать все это. Лучший способ научиться - запустить программу под отладчиком и наблюдать за ее поведением. Если вы предпочитаете читать Python, переходите к концу ответа :)
// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;
// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed only within MyFunction()
static int someLocalStaticVariable;
// Allocated on the stack each time MyFunction is called
// Deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
int someLocalVariable;
// A *pointer* is allocated on the stack each time MyFunction is called
// This pointer is deallocated when MyFunction returns
// scope - the pointer can be accessed only within MyFunction()
int* someDynamicVariable;
// This line causes space for an integer to be allocated in the heap
// when this line is executed. Note this is not at the beginning of
// the call to MyFunction(), like the automatic variables
// scope - only code within MyFunction() can access this space
// *through this particular variable*.
// However, if you pass the address somewhere else, that code
// can access it too
someDynamicVariable = new int;
// This line deallocates the space for the integer in the heap.
// If we did not write it, the memory would be "leaked".
// Note a fundamental difference between the stack and heap
// the heap must be managed. The stack is managed for us.
delete someDynamicVariable;
// In other cases, instead of deallocating this heap space you
// might store the address somewhere more permanent to use later.
// Some languages even take care of deallocation for you... but
// always it needs to be taken care of at runtime by some mechanism.
// When the function returns, someArgument, someLocalVariable
// and the pointer someDynamicVariable are deallocated.
// The space pointed to by someDynamicVariable was already
// deallocated prior to returning.
return;
}
// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.
Особенно ярким примером того, почему важно различать время жизни и область действия, является то, что переменная может иметь локальную область видимости, но статическое время жизни - например, someLocalStaticVariable в приведенном выше примере кода. Такие переменные могут сбить с толку наши обычные, но неформальные привычки именования. Например, когда мы говорим «местный», мы обычно имеем в виду «автоматически выделяемая переменная с локальной областью видимости», а когда мы говорим «глобальный», мы обычно имеем в виду «статически распределенная переменная с глобальным охватом». К сожалению, когда дело касается таких вещей, как «статически распределенные переменные с областью видимости файла», многие люди просто говорят… «Хм???».
Некоторые варианты синтаксиса в C / C++ усугубляют эту проблему - например, многие люди думают, что глобальные переменные не «статичны» из-за синтаксиса, показанного ниже.
int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation
int main() {return 0;}
Обратите внимание, что размещение ключевого слова static в объявлении выше предотвращает глобальную область видимости var2. Тем не менее, глобальная переменная var1 имеет статическое размещение. Это не интуитивно понятно! По этой причине я стараюсь никогда не использовать слово «статический» при описании области видимости, а вместо этого говорю что-то вроде области «файл» или «ограниченный файл». Однако многие люди используют фразу «статическая» или «статическая область видимости» для описания переменной, к которой можно получить доступ только из одного файла кода. В контексте времени жизни «статический» всегда означает, что переменная выделяется при запуске программы и освобождается при выходе из программы.
Некоторые люди считают эти концепции специфичными для C / C++. Они не. Например, пример Python ниже иллюстрирует все три типа распределения (в интерпретируемых языках возможны некоторые тонкие различия, которые я здесь не буду вдаваться).
from datetime import datetime
class Animal:
_FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated
def PetAnimal(self):
curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)
class Cat(Animal):
_FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's
class Dog(Animal):
_FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!
if __name__ == "__main__":
whiskers = Cat() # Dynamically allocated
fido = Dog() # Dynamically allocated
rinTinTin = Dog() # Dynamically allocated
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
Dog._FavoriteFood = 'milkbones'
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones
Я бы назвал статическую переменную, объявленную внутри функции, только локальной доступность, но обычно не использовал бы для нее термин «область видимости». Кроме того, может быть стоит отметить, что один аспект стека / кучи, с которым языки имеют практически нулевую гибкость: язык, который сохраняет контекст выполнения в стеке, не может использовать тот же стек для хранения вещей, которые должны будут пережить контексты, в которых они созданы . Некоторые языки, такие как PostScript
, имеют несколько стеков, но имеют «кучу», которая больше похожа на стек.
@supercat Все это имеет смысл. Я определил область видимости как «какие части кода могут доступ переменная» (и считаю, что это наиболее стандартное определение), поэтому я думаю, что мы согласны :)
Я бы рассматривал «объем» Переменная как ограниченный не только пространством, но и временем. Переменная в области класса-объекта должна сохранять свое значение, пока существует объект. Переменная в области контекста выполнения должна сохранять свое значение, пока выполнение остается в этом контексте. Объявление статической переменной создает идентификатор, область действия которого ограничена текущим блоком, который присоединяется к Переменная, область действия которого неограничена.
@supercat Вот почему я использую слово «время жизни», как я называю то, что вы называете временной областью. Это уменьшает необходимость перегружать слово «объем» таким количеством значений. Насколько я могу судить, похоже, что нет единого мнения относительно точных определений, даже среди канонических источников. Моя терминология частично заимствована из K&R, а частично из преобладающего использования на первом отделении CS, на котором я учился / преподавал. Всегда приятно услышать еще одно информированное мнение.
Один очень распространенный источник путаницы заключается в том, относится ли «переменная» к идентификатору, месту хранения или (для объектно-ориентированных структур) объекту кучи, который идентифицируется текущим содержимым места хранения. Автоматические переменные - это места хранения, которые возникают, когда выполнение входит в определенную область, и перестают существовать впоследствии. К статическим переменным может обращаться только имя в пределах их области видимости, но на многих языках функция может возвращать указатель на статическую переменную, а для кода в любом контексте, который имеет копию этого указателя ...
... иметь возможность получить доступ к переменной по желанию, независимо от того, имеет ли какой-либо аспект текущего контекста выполнения какое-либо отношение к функции, в которой была определена переменная.
ты, наверное, шутишь. вы действительно можете определить статическую переменную внутри функции?
@zaeemsattar абсолютно, и это не редкость, чтобы увидеть в коде C
@ZaeemSattar Думайте о статической переменной функции как о скрытом глобальном или как о частной статической переменной-члене.
Куча
Куча
Что такое стек?
Стопка - это куча предметов, обычно аккуратно уложенных.
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.
Что такое куча?
Куча - это неопрятное собрание вещей, скопившихся наугад.
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.
Оба вместе
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.
Что быстрее - стек или куча? И почему?
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.
Для новичков в программировании, вероятно, будет хорошей идеей использовать стек, поскольку это проще.
Поскольку стек небольшой, вы можете использовать его, когда точно знаете, сколько памяти вам понадобится для ваших данных, или если вы знаете, что размер ваших данных очень мал.
Лучше использовать кучу, когда вы знаете, что вам понадобится много памяти для ваших данных, или вы просто не уверены, сколько памяти вам понадобится (например, с динамическим массивом).
Стек - это область памяти, в которой хранятся локальные переменные (включая параметры метода). Когда дело доходит до объектных переменных, это просто ссылки (указатели) на фактические объекты в куче. Каждый раз, когда создается экземпляр объекта, часть памяти кучи выделяется для хранения данных (состояния) этого объекта. Поскольку объекты могут содержать другие объекты, некоторые из этих данных могут фактически содержать ссылки на эти вложенные объекты.
Поскольку некоторые ответы были придирками, я собираюсь внести свою лепту.
Удивительно, но никто не упомянул, что множественные (т.е. не связанные с количеством запущенных потоков уровня ОС) стеки вызовов можно найти не только в экзотических языках (PostScript) или платформах (Intel Itanium), но также в волокна, зеленые нити и некоторые реализации сопрограммы.
Волокна, зеленые нити и сопрограммы во многом похожи, что приводит к большой путанице. Разница между волокнами и зелеными потоками заключается в том, что первые используют кооперативную многозадачность, а вторые могут включать кооперативную или вытесняющую (или даже обе). Чтобы узнать о различиях между волокнами и сопрограммами, см. здесь.
В любом случае цель обоих волокон, зеленых потоков и сопрограмм состоит в том, чтобы несколько функций выполнялись одновременно, но нет параллельно (см. Раздел этот ТАК вопрос) в одном потоке уровня ОС, передавая управление друг от друга взад и вперед в одном потоке. организованная мода.
При использовании волокон, зеленых потоков или сопрограмм у обычно есть отдельный стек для каждой функции. (Технически, не только стек, но и весь контекст выполнения для каждой функции. Самое главное, регистры ЦП.) Для каждого потока существует столько стеков, сколько одновременно выполняющихся функций, и поток переключается между выполнением каждой функции. согласно логике вашей программы. Когда функция доходит до конца, ее стек уничтожается. Итак, количество и время жизни стеков являются динамическими, а не определяются количеством потоков уровня ОС!
Обратите внимание, что я сказал: «обычно имеет отдельный стек для каждой функции». Существуют реализации курсовых программ как сложен, так и без стека. Наиболее заметными реализациями C++ со стеком являются Boost.Coroutine и Microsoft PPLasync/await
. (Однако C++ возобновляемые функции (также известный как «async
и await
»), которые были предложены для C++ 17, вероятно, будут использовать сопрограммы без стека.)
Предложение волокон для стандартной библиотеки C++ готовится к публикации. Также есть сторонние библиотеки. Зеленые потоки чрезвычайно популярны в таких языках, как Python и Ruby.
В 1980-х годах UNIX распространилась, как кролики, с крупными компаниями, открывшими свои собственные. Один был у Exxon, как и у десятков брендов, утраченных историей. Расположение памяти оставалось на усмотрение многих разработчиков.
Типичная программа на C была размещена в памяти с помощью возможность увеличения за счет изменения значения brk (). Обычно HEAP был чуть ниже этого значения brk. а увеличение brk увеличивало количество доступной кучи.
Единственный СТЕК обычно был областью ниже HEAP, которая была участком памяти. не содержит ничего ценного до вершины следующего фиксированного блока памяти. Следующим блоком часто был КОД, который мог быть перезаписан данными стека. в одном из знаменитых хаков своего времени.
Одним из типичных блоков памяти был BSS (блок нулевых значений). что случайно не было обнулено ни в одном предложении производителя. Другой был DATA, содержащий инициализированные значения, включая строки и числа. Третьим был CODE, содержащий CRT (среда выполнения C), основные функции и библиотеки.
Появление виртуальной памяти в UNIX меняет многие ограничения. Нет объективной причины, по которой эти блоки должны быть смежными, либо фиксированного размера, либо заказанного особым образом сейчас. Конечно, до UNIX была Multics, которая не страдала от этих ограничений. Вот схема, показывающая одну из схем памяти той эпохи.
Пара копеек: Думаю, хорошо бы память рисовать графически и попроще:
Стрелки - показывают, где растут стек и куча, размер стека процесса имеет ограничение, определенное в ОС, размер стека потока обычно ограничивается параметрами в API создания потока. Куча обычно ограничивается максимальным размером виртуальной памяти процесса, например, для 32-битных 2-4 GB.
Очень простой способ: куча процессов является общей для процесса и всех потоков внутри, используется для выделения памяти в общем случае с чем-то вроде malloc ().
Стек - это быстрая память для хранения в общем случае указателей возврата функций и переменных, обрабатываемых как параметры при вызове функции, локальных переменных функции.
OK, просто и коротко они означают упорядоченный и не заказан ...!
Куча: В элементах стека вещи накладываются друг на друга, это означает, что обработка будет быстрее и эффективнее! ...
Таким образом, всегда есть указатель, указывающий на конкретный элемент, также обработка будет быстрее, между элементами также есть взаимосвязь! ...
Куча: Нет порядка, обработка будет медленнее, и значения перепутаны вместе без определенного порядка или индекса ... есть случайные, и нет никакой связи между ними ... поэтому время выполнения и использования может варьироваться ...
Я также создаю изображение ниже, чтобы показать, как они могут выглядеть:
Многие ответы правильны как концепции, но мы должны отметить, что стек необходим аппаратному обеспечению (то есть микропроцессору), чтобы разрешить вызов подпрограмм (CALL на языке ассемблера ..). (Ребята из ООП назовут это методы)
В стеке вы сохраняете адреса возврата, а call → push / ret → pop управляется непосредственно аппаратно.
Вы можете использовать стек для передачи параметров ... даже если он медленнее, чем использование регистров (скажет ли гуру микропроцессоров или хорошая книга BIOS 1980-х годов ...)
Использование стека происходит быстрее:
Что такое OPP? Вы имеете в виду ООП (объектно-ориентированного программирования)?
Вы хотите сказать, что malloc
- это вызов ядра?
1) да, извините ... ООП ... 2) malloc: я пишу вкратце, извините ... malloc находится в пользовательском пространстве ... но может запускать другие вызовы ... дело в том, что использование кучи МОЖЕТ быть очень медленным ...
«Многие ответы верны как концепции, но мы должны отметить, что стек необходим аппаратному обеспечению (то есть микропроцессору), чтобы разрешить вызов подпрограмм (CALL на языке ассемблера ..)». Вы путаете стек ЦП (если он есть в современном ЦП) и стеки времени выполнения языка (по одному на поток). Когда программисты говорят о стеке, это стек выполнения потока среды выполнения, например стек потока NET), мы не говорим о стеке ЦП.
«NET thread» - это не настоящий стек. (то же самое для JVM): это концепции SW. (другие называют это «активационной записью») Мы должны начать с реальных схем, как это было в истории ПК, чтобы получить реальное понимание.
Мне есть чем поделиться, хотя основные моменты уже освещены.
Куча
Куча
Интересное примечание:
лаконично и чисто. отлично:)
Ух ты! Так много ответов, и я не думаю, что кто-то из них правильно понял ...
1) Где и что они (физически в памяти реального компьютера)?
Стек - это память, которая начинается с наивысшего адреса памяти, выделенного для образа вашей программы, а затем его значение уменьшается оттуда. Он зарезервирован для параметров вызываемой функции и для всех временных переменных, используемых в функциях.
Есть две кучи: публичная и приватная.
Частная куча начинается на 16-байтовой границе (для 64-разрядных программ) или 8-байтовой границе (для 32-разрядных программ) после последнего байта кода в вашей программе, а затем увеличивается в значении оттуда. Ее также называют кучей по умолчанию.
Если частная куча становится слишком большой, она будет перекрывать область стека, как и стек, если он станет слишком большим. Поскольку стек начинается с более высокого адреса и спускается к более низкому адресу, при правильном взломе вы можете сделать стек настолько большим, что он будет выходить за пределы частной области кучи и перекрывать область кода. Уловка тогда состоит в том, чтобы перекрыть достаточно области кода, чтобы вы могли зацепить код. Это немного сложно сделать, и вы рискуете привести к сбою программы, но это просто и очень эффективно.
Общедоступная куча находится в собственном пространстве памяти вне пространства образа вашей программы. Именно эта память будет выгружена на жесткий диск, если ресурсы памяти станут недостаточными.
2) В какой степени они контролируются ОС или языковой средой?
Стек контролируется программистом, частная куча управляется ОС, а общедоступная куча никем не контролируется, потому что это служба ОС - вы делаете запросы, и они либо предоставляются, либо отклоняются.
2б) Каков их объем?
Все они глобальны для программы, но их содержимое может быть частным, общедоступным или глобальным.
2в) От чего зависит размер каждого из них?
Размер стека и частной кучи определяется параметрами среды выполнения вашего компилятора. Общедоступная куча инициализируется во время выполнения с помощью параметра размера.
2г) Что делает человека быстрее?
Они не созданы для того, чтобы быть быстрыми, они созданы для того, чтобы быть полезными. От того, как программист их использует, зависит, будут они «быстрыми» или «медленными».
ССЫЛКА:
https://norasandler.com/2019/02/18/Write-a-Compiler-10.html
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapi-getprocessheap
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapi-heapcreate
The stack is essentially an easy-to-access memory that simply manages its items as a - well - stack. Only items for which the size is known in advance can go onto the stack. This is the case for numbers, strings, booleans.
The heap is a memory for items of which you can’t predetermine the exact size and structure. Since objects and arrays can be mutated and change at runtime, they have to go into the heap.
Источник: Academind
Спасибо за действительно хорошее обсуждение, но как настоящий новичок мне интересно, где хранятся инструкции? В НАЧАЛЕ ученые выбирали между двумя архитектурами (фон NEUMANN, где все считается ДАННЫМИ, и HARVARD, где одна область памяти была зарезервирована для инструкций, а другая - для данных). В конечном итоге мы выбрали дизайн фон Неймана, и теперь все считается «одинаковым». Мне было трудно, когда я учился сборке https://www.cs.virginia.edu/~evans/cs216/guides/x86.html потому что они говорят о регистрах и указателях стека.
Все выше говорит о ДАННЫХ. Я предполагаю, что, поскольку инструкция - это определенная вещь с определенным объемом памяти, она будет помещена в стек, и поэтому все «эти» регистры, обсуждаемые при сборке, находятся в стеке. Конечно, затем пришло объектно-ориентированное программирование с инструкциями и данными, объединенными в структуру, которая была динамической, поэтому теперь инструкции также будут храниться в куче?
AFAIK, все еще существует много процессоров с гарвардской архитектурой (обычно оптимизированные DSP), которые имеют разделенную память (и шину) для инструкций и данных. (Некоторые из них даже более 1 памяти данных). Это сделано для оптимизации продолжительности цикла (выборка инструкций, выборка данных и выполнение предыдущей инструкции - все в одном машинном цикле), таким образом, используя преимущества того, что они называют конвейерной связью. Я не знаю, что инструкции должны каким-либо образом помещаться в стек. В любом случае, у меня есть подлое предчувствие, что этот ответ может немного выйти за рамки исходного вопроса ...
Стек и куча ЦП физически связаны с тем, как ЦП и регистры работают с памятью, как работает язык машинного ассемблера, а не сами языки высокого уровня, даже если эти языки могут решать мелочи.
Все современные процессоры работают с «той же» теорией микропроцессоров: все они основаны на так называемых «регистрах», а некоторые предназначены для «стека» для повышения производительности. У всех процессоров с самого начала есть стековые регистры, и, как я знаю, они всегда были здесь, так сказать. Языки ассемблера одинаковы с самого начала, несмотря на различия ... вплоть до Microsoft и его промежуточного языка (IL), который изменил парадигму, чтобы иметь язык ассемблера виртуальных машин OO. Так что в будущем мы сможем иметь некоторый процессор CLI / CIL (один проект MS).
ЦП имеют регистры стека для ускорения доступа к памяти, но они ограничены по сравнению с использованием других регистров для получения полного доступа ко всей доступной памяти для процесса. Вот почему мы говорили о выделении стека и кучи.
Таким образом, куча является большой и медленной и предназначена для «глобальных» экземпляров и содержимого объектов, поскольку стек небольшой и быстрый, а также для «локальных» переменных и ссылок (скрытых указателей, чтобы забыть управлять ими).
Итак, когда мы используем ключевое слово new в методе, ссылка (int) создается в стеке, но объект и все его содержимое (типы значений, а также объекты) создаются в куче, если я помню. Но в стеке создаются локальные элементарные типы значений и массивы.
Разница в доступе к памяти заключается на уровне ссылок на ячейки: адресация кучи, общей памяти процесса, требует большей сложности с точки зрения обработки регистров ЦП, чем стек, который «более» локально с точки зрения адресации, потому что стек ЦП Регистр используется как базовый адрес, если я помню.
Вот почему, когда у нас очень длинные или бесконечные рекурсивные вызовы или циклы, у нас происходит быстрое переполнение стека, без зависания системы на современных компьютерах ...
C# Heap (ing) против стека (ing) в .NET
Стек против кучи: узнайте разницу
Выделение памяти статическим классом, где она хранится C#
https://en.wikipedia.org/wiki/Memory_management
https://en.wikipedia.org/wiki/Stack_register
Ресурсы на ассемблере:
Учебник по программированию сборки
Руководства для разработчиков программного обеспечения для архитектур Intel® 64 и IA-32
действительно хорошее объяснение можно найти здесь В чем разница между стеком и кучей?