Книги по языку программирования объясняют, что типы значений создаются в стеке , а ссылочные типы создаются в куче , без объяснения того, что представляют собой эти две вещи. Я не прочитал четкое объяснение этого. Я понимаю, что такое стек . Но,
- Где и что они (физически в памяти реального компьютера)?
- В какой степени они контролируются ОС или языком выполнения?
- Какова их сфера применения?
- От чего зависит размер каждого из них?
- Что делает быстрее?
rlimit_stack
. Также смотрите Red Hat Issue 1463241Ответы:
Стек - это память, выделенная как пустое место для потока выполнения. Когда вызывается функция, блок резервируется в верхней части стека для локальных переменных и некоторых бухгалтерских данных. Когда эта функция возвращается, блок становится неиспользуемым и может использоваться при следующем вызове функции. Стек всегда зарезервирован в порядке LIFO (последний в порядке поступления); последний зарезервированный блок всегда является следующим блоком, который должен быть освобожден. Это действительно упрощает отслеживание стека; освобождение блока из стека - это не более, чем настройка одного указателя.
Куча памяти выделяется для динамического выделения. В отличие от стека, нет принудительного шаблона для выделения и освобождения блоков из кучи; Вы можете выделить блок в любое время и освободить его в любое время. Это значительно усложняет отслеживание того, какие части кучи выделены или свободны в любой момент времени; Есть много пользовательских распределителей кучи, доступных для настройки производительности кучи для различных моделей использования.
Каждый поток получает стек, в то время как для приложения обычно есть только одна куча (хотя весьма часто иметь несколько куч для разных типов размещения).
Чтобы ответить на ваши вопросы напрямую:
ОС выделяет стек для каждого потока системного уровня при его создании. Обычно ОС вызывается языковой средой выполнения для выделения кучи для приложения.
Стек присоединен к потоку, поэтому, когда поток выходит из стека, он освобождается. Куча обычно выделяется при запуске приложения средой выполнения и восстанавливается при выходе из приложения (технически процесса).
Размер стека устанавливается при создании потока. Размер кучи устанавливается при запуске приложения, но может увеличиваться по мере необходимости (распределитель запрашивает больше памяти у операционной системы).
Стек работает быстрее, потому что шаблон доступа упрощает выделение и освобождение памяти из него (указатель / целое число просто увеличивается или уменьшается), в то время как куча имеет гораздо более сложную бухгалтерию, вовлеченную в распределение или освобождение. Кроме того, каждый байт в стеке имеет тенденцию использоваться очень часто, что означает, что он, как правило, отображается в кэш процессора, что делает его очень быстрым. Другим ударом производительности для кучи является то, что куча, являющаяся главным образом глобальным ресурсом, как правило, должна быть многопоточной безопасной, то есть каждое распределение и освобождение должно быть - как правило - синхронизировано со «всеми» другими обращениями к куче в программе.
Наглядная демонстрация:
Источник изображения: vikashazrati.wordpress.com
источник
стек:
Heap:
delete
,delete[]
илиfree
.new
илиmalloc
соответственно.Пример:
источник
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Наиболее важным моментом является то, что куча и стек являются общими терминами для способов выделения памяти. Они могут быть реализованы многими различными способами, и эти термины применяются к основным понятиям.
В пачке предметов предметы располагаются один над другим в том порядке, в котором они были там размещены, и вы можете удалить только верхний (не переворачивая все это).
Простота стека заключается в том, что вам не нужно поддерживать таблицу, содержащую запись каждого раздела выделенной памяти; единственная информация о состоянии, которая вам нужна, - это единственный указатель на конец стека. Чтобы выделить и удалить, вы просто увеличиваете и уменьшаете этот единственный указатель. Примечание: иногда может быть реализован стек, который начинается в верхней части раздела памяти и расширяется вниз, а не растет вверх.
В куче нет определенного порядка расположения элементов. Вы можете заходить и убирать предметы в любом порядке, потому что нет четкого «верхнего» предмета.
Выделение кучи требует ведения полной записи о том, какая память выделена, а что нет, а также некоторых служебных расходов для уменьшения фрагментации, поиска смежных сегментов памяти, достаточно больших, чтобы соответствовать требуемому размеру, и так далее. Память может быть освобождена в любое время, оставляя свободное место. Иногда распределитель памяти выполняет задачи обслуживания, такие как дефрагментация памяти, перемещая выделенную память или собирая мусор - определяя во время выполнения, когда память больше не находится в области действия, и освобождая ее.
Эти образы должны довольно хорошо описать два способа выделения и освобождения памяти в стеке и куче. Yum!
В какой степени они контролируются ОС или языковой средой выполнения?
Как уже упоминалось, куча и стек являются общими терминами и могут быть реализованы многими способами. Компьютерные программы обычно имеют стек, называемый стеком вызовов, который хранит информацию, относящуюся к текущей функции, такую как указатель на функцию, из которой она была вызвана, и любые локальные переменные. Поскольку функции вызывают другие функции и затем возвращаются, стек увеличивается и сжимается, чтобы хранить информацию от функций дальше по стеку вызовов. Программа на самом деле не имеет контроля над ней во время выполнения; это определяется языком программирования, ОС и даже архитектурой системы.
Куча - это общий термин, используемый для любой памяти, которая выделяется динамически и случайным образом; т.е. не в порядке. Память обычно выделяется ОС, при этом приложение вызывает функции API для этого. При управлении динамически выделяемой памятью требуются значительные накладные расходы, которые обычно обрабатываются кодом времени выполнения используемого языка программирования или среды.
Какова их сфера применения?
Стек вызовов является настолько низкоуровневой концепцией, что он не имеет отношения к «объему» в смысле программирования. Если вы разберете некоторый код, вы увидите относительные ссылки на стиль указателя на части стека, но, что касается языка более высокого уровня, язык налагает свои собственные правила области видимости. Однако одним важным аспектом стека является то, что, как только функция возвращается, все, что является локальным для этой функции, немедленно освобождается из стека. Это работает так, как вы и ожидаете, учитывая работу ваших языков программирования. В куче это тоже сложно определить. Область действия - это то, что доступно операционной системе, но ваш язык программирования, вероятно, добавляет свои правила о том, что такое «область действия» в вашем приложении. Архитектура процессора и ОС используют виртуальную адресацию, который процессор преобразует в физические адреса, есть ошибки страниц и т. д. Они отслеживают, какие страницы принадлежат каким приложениям. Однако вам никогда не нужно беспокоиться об этом, потому что вы просто используете любой метод, используемый вашим языком программирования, для выделения и освобождения памяти, и проверяете на наличие ошибок (если по какой-либо причине распределение / освобождение не удается).
От чего зависит размер каждого из них?
Опять же, это зависит от языка, компилятора, операционной системы и архитектуры. Стек обычно выделяется заранее, потому что по определению он должен быть смежной памятью. Компилятор языка или ОС определяют его размер. Вы не храните огромные порции данных в стеке, поэтому они будут достаточно большими, чтобы никогда не использовать их полностью, за исключением случаев нежелательной бесконечной рекурсии (отсюда «переполнение стека») или других необычных программных решений.
Куча - это общий термин для всего, что может быть динамически выделено. В зависимости от того, как вы на это смотрите, размер постоянно меняется. В современных процессорах и операционных системах точный способ его работы в любом случае очень абстрагирован, поэтому обычно вам не нужно сильно беспокоиться о том, как он работает в глубине, за исключением того, что (в языках, где это позволяет) вы не должны использовать память, которая Вы еще не распределили или память, которую вы освободили.
Что делает быстрее?
Стек быстрее, потому что вся свободная память всегда смежна. Не нужно поддерживать список всех сегментов свободной памяти, только один указатель на текущую вершину стека. Для этого компиляторы обычно хранят этот указатель в специальном быстром регистре . Более того, последующие операции над стеком обычно сосредоточены в очень близких областях памяти, что на очень низком уровне хорошо для оптимизации кэш-памятью процессора.
источник
(Я переместил этот ответ из другого вопроса, который был более или менее обманчивым.)
Ответ на ваш вопрос зависит от конкретной реализации и может варьироваться в зависимости от компилятора и архитектуры процессора. Однако вот упрощенное объяснение.
Куча
new
илиmalloc
) выполняются путем создания подходящего блока из одного из свободных блоков. Это требует обновления списка блоков в куче. Эта мета информация о блоках в куче также хранится в куче, часто в небольшой области перед каждым блоком.Стек
Нет, записи активации для функций (то есть локальных или автоматических переменных) размещаются в стеке, который используется не только для хранения этих переменных, но и для отслеживания вызовов вложенных функций.
То, как управляется куча, зависит от среды выполнения. C использует
malloc
и C ++ используетnew
, но многие другие языки имеют сборку мусора.Однако стек является более низкоуровневой функцией, тесно связанной с архитектурой процессора. Растить кучу, когда не хватает места, не так уж сложно, так как это может быть реализовано в вызове библиотеки, которая обрабатывает кучу. Однако увеличение стека часто невозможно, так как переполнение стека обнаруживается только тогда, когда это слишком поздно; и закрытие потока выполнения - единственно возможный вариант.
источник
В следующем коде C #
Вот как управляется память
Local Variables
это должно продолжаться до тех пор, пока вызов функции идет в стек. Куча используется для переменных, время жизни которых мы на самом деле не знаем заранее, но мы ожидаем, что они будут длиться некоторое время. В большинстве языков очень важно, чтобы мы знали во время компиляции, насколько велика переменная, если мы хотим сохранить ее в стеке.Объекты (размер которых изменяется по мере их обновления) попадают в кучу, потому что мы не знаем во время создания, как долго они будут длиться. Во многих языках куча мусора собирается для поиска объектов (таких как объект cls1), на которые больше нет ссылок.
В Java большинство объектов попадают прямо в кучу. В таких языках, как C / C ++, структуры и классы часто могут оставаться в стеке, когда вы не имеете дело с указателями.
Более подробную информацию можно найти здесь:
Разница между выделением стека и кучи памяти «timmurphy.org
и здесь:
Создание объектов в стеке и куче
Эта статья является источником изображения выше: Шесть важных концепций .NET: стек, куча, типы значений, ссылочные типы, упаковка и распаковка - CodeProject
но имейте в виду, что это может содержать некоторые неточности.
источник
Стек Когда вы вызываете функцию, в стек помещаются аргументы этой функции и другие накладные расходы. Некоторая информация (например, куда идти по возвращении) также хранится там. Когда вы объявляете переменную внутри вашей функции, эта переменная также размещается в стеке.
Распределение стека довольно просто, потому что вы всегда освобождаете в обратном порядке, в котором вы размещаете. Набор стеков добавляется при вводе функций, соответствующие данные удаляются при выходе из них. Это означает, что вы склонны оставаться в небольшой области стека, если вы не вызываете много функций, которые вызывают множество других функций (или не создаете рекурсивное решение).
Куча Куча - это общее имя для размещения данных, которые вы создаете на лету. Если вы не знаете, сколько космических кораблей собирается создать ваша программа, вы, вероятно, будете использовать оператор new (или malloc или эквивалентный) для создания каждого космического корабля. Это распределение останется на некоторое время, поэтому, скорее всего, мы освободим вещи в другом порядке, чем мы их создали.
Таким образом, куча намного более сложна, потому что в конечном итоге возникают области памяти, которые не используются, чередуются с фрагментами, которые - фрагментация памяти. Поиск свободной памяти нужного вам размера - сложная проблема. Вот почему следует избегать кучи (хотя она все еще часто используется).
Реализация Реализация как стека, так и кучи обычно зависит от времени выполнения / ОС. Часто игры и другие приложения, критически важные для производительности, создают свои собственные решения для памяти, которые захватывают большой кусок памяти из кучи и затем распределяют ее изнутри, чтобы не полагаться на ОС для памяти.
Это практично только в том случае, если использование вашей памяти сильно отличается от нормы - то есть для игр, в которых вы загружаете уровень за одну огромную операцию и можете отбросить все это за другую огромную операцию.
Физическое расположение в памяти Это менее важно, чем вы думаете, из-за технологии, называемой виртуальной памятью, которая заставляет вашу программу думать, что у вас есть доступ к определенному адресу, где физические данные находятся где-то еще (даже на жестком диске!). Адреса, которые вы получаете для стека, растут по мере того, как дерево вызовов становится глубже. Адреса для кучи непредсказуемы (т. Е. Специфичны для имплиментации) и, честно говоря, не важны.
источник
Чтобы уточнить, этот ответ имеет неверную информацию ( Томас исправил свой ответ после комментариев, круто :)). Другие ответы просто не объясняют, что означает статическое распределение. Поэтому ниже я объясню три основных формы распределения и их связь с кучей, стеком и сегментом данных. Я также покажу некоторые примеры на C / C ++ и Python, чтобы помочь людям понять.
«Статические» (статически распределенные AKA) переменные не размещаются в стеке. Не думайте так - многие люди делают это только потому, что «статический» звук очень похож на «стек». Они на самом деле не существуют ни в стеке, ни в куче. Являются частью того, что называется сегментом данных .
Тем не менее, как правило, лучше учитывать " объем " и " срок службы », а не «стек» и «куча».
Область действия относится к тому, какие части кода могут обращаться к переменной. Обычно мы думаем о локальной области действия (доступ к которой может получить только текущая функция) в сравнении с глобальной областью действия (можно получить доступ в любом месте), хотя область действия может стать намного более сложной.
Время жизни относится к тому, когда переменная выделяется и освобождается во время выполнения программы. Обычно мы думаем о статическом распределении (переменная будет сохраняться в течение всей продолжительности программы, что делает ее полезной для хранения одной и той же информации при нескольких вызовах функций) в сравнении с автоматическим распределением (переменная сохраняется только во время одного вызова функции, что делает ее полезной для хранение информации, которая используется только во время вашей функции и может быть отброшена, когда вы закончите) в сравнении с динамическим распределением (переменные, продолжительность которых определяется во время выполнения, а не во время компиляции, как статические или автоматические).
Хотя большинство компиляторов и интерпретаторов реализуют это поведение аналогично с точки зрения использования стеков, куч и т. Д., Компилятор может иногда нарушать эти соглашения, если он хочет, пока поведение корректно. Например, из-за оптимизации локальная переменная может существовать только в регистре или может быть полностью удалена, даже если большинство локальных переменных существует в стеке. Как было отмечено в нескольких комментариях, вы можете свободно реализовать компилятор, который даже не использует стек или кучу, а вместо этого использует некоторые другие механизмы хранения (редко делается, поскольку стеки и кучи отлично подходят для этого).
Я приведу простой аннотированный C-код для иллюстрации всего этого. Лучший способ научиться - это запускать программу под отладчиком и наблюдать за ее поведением. Если вы предпочитаете читать Python, перейдите к концу ответа :)
Особенно острым примером того, почему важно различать время жизни и область видимости, является то, что переменная может иметь локальную область видимости, но статическое время жизни - например, «someLocalStaticVariable» в приведенном выше примере кода. Такие переменные могут сделать наши общие, но неформальные привычки именования очень запутанными. Например, когда мы говорим « локально », мы обычно имеем в виду « локально распределенную переменную с автоматическим распределением », а когда мы говорим «глобально», мы обычно имеем в виду « глобально ограниченную статически распределенную переменную ». К сожалению, когда речь заходит о таких вещах, как « статически распределенные переменные в области видимости файлов », многие люди просто говорят… « да ??? ».
Некоторые из вариантов синтаксиса в C / C ++ усугубляют эту проблему - например, многие люди думают, что глобальные переменные не являются «статическими» из-за синтаксиса, показанного ниже.
Обратите внимание, что добавление ключевого слова «static» в объявлении выше предотвращает глобальную область видимости var2. Тем не менее, глобальное var1 имеет статическое размещение. Это не интуитивно понятно! По этой причине я стараюсь никогда не использовать слово «статический» при описании области действия, а вместо этого говорю что-то вроде «файл» или «ограниченный файл» область. Однако многие люди используют фразу «статический» или «статический объем» для описания переменной, доступ к которой возможен только из одного файла кода. В контексте времени жизни «статический» всегда означает, что переменная выделяется при запуске программы и освобождается при выходе из программы.
Некоторые люди считают эти понятия специфичными для C / C ++. Они не. Например, приведенный ниже пример Python иллюстрирует все три типа размещения (в интерпретируемых языках возможны некоторые тонкие различия, о которых я не буду здесь говорить).
источник
PostScript
имеют несколько стеков, но имеют «кучу», которая ведет себя больше как стек.Другие довольно хорошо ответили на широкие удары, поэтому я добавлю несколько деталей.
Стек и куча не обязательно должны быть единичными. Распространенная ситуация, когда у вас есть более одного стека, если у вас есть более одного потока в процессе. В этом случае каждый поток имеет свой собственный стек. Вы также можете иметь более одной кучи, например, некоторые конфигурации DLL могут приводить к тому, что разные DLL распределяются из разных куч, поэтому обычно плохая идея освобождать память, выделенную другой библиотекой.
В C вы можете получить выгоду от распределения переменной длины за счет использования alloca , которая выделяет в стеке, в отличие от alloc, которая выделяет в куче. Эта память не выдержит вашего оператора return, но она полезна для чистого буфера.
Создание огромного временного буфера в Windows, который вы не используете, не является бесплатным. Это связано с тем, что компилятор будет генерировать цикл проверки стека, который вызывается каждый раз при вводе вашей функции, чтобы убедиться, что стек существует (поскольку Windows использует одну защитную страницу в конце стека для определения того, когда ему нужно увеличить стек. Если вы обращаетесь к памяти более чем на одну страницу с конца стека, вы потерпите крах). Пример:
источник
alloca
?Другие прямо ответили на ваш вопрос, но, пытаясь понять стек и кучу, я думаю, что полезно рассмотреть структуру памяти традиционного процесса UNIX (без потоков и
mmap()
распределителей на основе). Управление памятью Глоссарий веб - страница имеет диаграмму этой схемы памяти.Стек и куча традиционно расположены на противоположных концах виртуального адресного пространства процесса. При доступе к стеку размер автоматически увеличивается до размера, установленного ядром (который можно настроить с помощью
setrlimit(RLIMIT_STACK, ...)
). Куча увеличивается, когда распределитель памяти вызывает системный вызовbrk()
илиsbrk()
, отображая больше страниц физической памяти в виртуальное адресное пространство процесса.В системах без виртуальной памяти, таких как некоторые встроенные системы, часто применяется одна и та же базовая схема, за исключением того, что размер стека и кучи фиксирован. Однако в других встроенных системах (например, основанных на микроконтроллерах PIC от Microchip) программный стек представляет собой отдельный блок памяти, который не адресуется инструкциями перемещения данных, и может быть изменен или прочитан только косвенно с помощью инструкций потока программы (вызов, возврат и т. д.). Другие архитектуры, такие как процессоры Intel Itanium, имеют несколько стеков . В этом смысле стек является элементом архитектуры ЦП.
источник
Что такое стек?
Стек - это куча объектов, обычно аккуратно расположенных.
Что такое куча?
Куча - это неопрятная коллекция беспорядочно скопившихся вещей.
Оба вместе
Что быстрее - стек или куча? И почему?
Для новичков в программировании, вероятно, будет хорошей идеей использовать стек, поскольку он проще.
Поскольку стек небольшой, его можно использовать, когда вы точно знаете, сколько памяти вам понадобится для ваших данных, или если вы знаете, что размер ваших данных очень мал.
Лучше использовать кучу, когда вы знаете, что вам понадобится много памяти для ваших данных, или вы просто не уверены, сколько памяти вам понадобится (например, с динамическим массивом).
Модель памяти Java
Стек - это область памяти, в которой хранятся локальные переменные (включая параметры метода). Когда дело доходит до переменных объекта, это всего лишь ссылки (указатели) на реальные объекты в куче.
Каждый раз, когда создается экземпляр объекта, часть памяти кучи откладывается для хранения данных (состояния) этого объекта. Поскольку объекты могут содержать другие объекты, некоторые из этих данных могут фактически содержать ссылки на эти вложенные объекты.
источник
Стек - это часть памяти, которой можно манипулировать с помощью нескольких инструкций языка ассемблера, таких как «pop» (удалить и вернуть значение из стека) и «push» (передать значение в стек), но также вызвать ( вызвать подпрограмму - это толкает адрес для возврата в стек) и return (возврат из подпрограммы - это извлекает адрес из стека и переходит к нему). Это область памяти ниже регистра указателя стека, который может быть установлен по мере необходимости. Стек также используется для передачи аргументов подпрограммам, а также для сохранения значений в регистрах перед вызовом подпрограмм.
Куча - это часть памяти, которая предоставляется приложению операционной системой, обычно через системный вызов типа malloc. В современных ОС эта память представляет собой набор страниц, к которым имеет доступ только вызывающий процесс.
Размер стека определяется во время выполнения и, как правило, не увеличивается после запуска программы. В программе на Си стек должен быть достаточно большим, чтобы вместить каждую переменную, объявленную внутри каждой функции. Куча будет динамически расти по мере необходимости, но ОС в конечном итоге сделает вызов (она часто будет увеличивать кучу больше, чем запрашивает malloc, так что по крайней мере некоторым будущим malloc не придется возвращаться к ядру для получить больше памяти. Такое поведение часто настраивается)
Поскольку вы выделили стек перед запуском программы, вам никогда не понадобится malloc, прежде чем вы сможете использовать стек, так что это небольшое преимущество. На практике очень трудно предсказать, что будет быстрым, а что медленным, в современных операционных системах с подсистемами виртуальной памяти, потому что то, как страницы реализованы и где они хранятся, является деталью реализации.
источник
Я думаю, что многие другие люди дали вам в основном правильные ответы по этому вопросу.
Одна деталь, которая была упущена, однако, состоит в том, что «куча» на самом деле должна, вероятно, называться «бесплатным магазином». Причина этого различия заключается в том, что исходное бесплатное хранилище было реализовано со структурой данных, известной как «биноминальная куча». По этой причине выделение из ранних реализаций malloc () / free () было выделением из кучи. Однако в наши дни большинство бесплатных магазинов реализованы с очень сложными структурами данных, которые не являются биномиальными кучами.
источник
C
язык требует использования «стека» . Это распространенное заблуждение, хотя это (безусловно) доминирующая парадигма для реализацииC99 6.2.4 automatic storage duration objects
(переменных). На самом деле, слово «стек» даже не встречается вC99
языковом стандарте: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdfВы можете делать некоторые интересные вещи со стеком. Например, у вас есть такие функции, как alloca (при условии, что вы можете обойти обильные предупреждения, касающиеся его использования), которая является формой malloc, которая специально использует стек, а не кучу памяти.
Тем не менее, ошибки памяти на основе стека являются одними из худших, которые я испытал. Если вы используете кучную память и выходите за границы выделенного блока, у вас есть хороший шанс вызвать ошибку сегмента. (Не 100%: ваш блок может быть случайно смежным с другим, который вы ранее разместили.) Но так как переменные, созданные в стеке, всегда смежны друг с другом, выписывание границ может изменить значение другой переменной. Я узнал, что всякий раз, когда я чувствую, что моя программа перестала подчиняться законам логики, это, вероятно, переполнение буфера.
источник
alloca
? Например, это работает на Windows? Это только для Unix-подобных операционных систем?Просто в стеке создаются локальные переменные. Кроме того, каждый раз, когда вы вызываете подпрограмму, счетчик программы (указатель на следующую машинную инструкцию) и любые важные регистры, а иногда и параметры помещаются в стек. Затем любые локальные переменные внутри подпрограммы помещаются в стек (и используются оттуда). Когда подпрограмма заканчивается, все эти вещи возвращаются из стека. ПК и регистр данных получаются и возвращаются туда, где они были, как сейчас, чтобы ваша программа могла идти своим путем.
Куча - это область динамического выделения памяти (явные вызовы «new» или «allocate»). Это специальная структура данных, которая может отслеживать блоки памяти разных размеров и их статус распределения.
В «классических» системах ОЗУ было расположено так, что указатель стека начинался с нижней части памяти, указатель кучи начинался сверху, и они росли друг к другу. Если они перекрываются, у вас недостаточно оперативной памяти. Это не работает с современными многопоточными ОС, хотя. Каждый поток должен иметь свой собственный стек, и они могут создаваться динамически.
источник
Из WikiAnwser.
стек
Когда функция или метод вызывает другую функцию, которая, в свою очередь, вызывает другую функцию и т. Д., Выполнение всех этих функций приостанавливается до тех пор, пока самая последняя функция не вернет свое значение.
Эта цепочка приостановленных вызовов функций является стеком, поскольку элементы в стеке (вызовы функций) зависят друг от друга.
Стек важно учитывать при обработке исключений и выполнении потоков.
отвал
Куча - это просто память, используемая программами для хранения переменных. Элемент кучи (переменные) не имеет зависимостей друг от друга и всегда может быть доступен случайным образом в любое время.
источник
стек
отвал
источник
ОК, просто и в двух словах, они означают упорядоченный, а не заказанный ...!
Стек : В элементах стека вещи оказываются друг на друге, что означает, что они будут быстрее и эффективнее обрабатываться! ...
Таким образом, всегда есть индекс, указывающий на конкретный элемент, также обработка будет быстрее, также есть связь между элементами! ...
Куча : нет порядка, обработка будет медленнее, а значения перепутаны без какого-либо определенного порядка или индекса ... есть случайные и нет никакой связи между ними ... поэтому время выполнения и использования может варьироваться ...
Я также создаю изображение ниже, чтобы показать, как они могут выглядеть:
источник
Короче говоря
Стек используется для статического выделения памяти и куча для динамического выделения памяти, которые хранятся в оперативной памяти компьютера.
В деталях
Стек
Стек представляет собой структуру данных «LIFO» (последний пришел, первый вышел), которая управляется и оптимизируется центральным процессором довольно близко. Каждый раз, когда функция объявляет новую переменную, она «помещается» в стек. Затем каждый раз при выходе из функции все переменные, помещаемые в стек этой функцией, освобождаются (то есть они удаляются). После освобождения переменной стека эта область памяти становится доступной для других переменных стека.
Преимущество использования стека для хранения переменных заключается в том, что память управляется за вас. Вам не нужно выделять память вручную или освобождать ее, если она вам больше не нужна. Более того, поскольку процессор эффективно организует стековую память, чтение и запись в переменные стека происходит очень быстро.
Больше можно найти здесь .
Куча
Куча - это область памяти вашего компьютера, которая не управляется автоматически и не так жестко управляется процессором. Это более свободно плавающая область памяти (и больше). Чтобы выделить память в куче, вы должны использовать malloc () или calloc (), которые являются встроенными функциями C. После выделения памяти в куче вы несете ответственность за использование free () для освобождения этой памяти, когда она вам больше не нужна.
Если вы этого не сделаете, ваша программа будет иметь то, что известно как утечка памяти. То есть память в куче все равно будет выделена (и не будет доступна другим процессам). Как мы увидим в разделе отладки, есть инструмент под названием Valgrind, который может помочь вам обнаружить утечки памяти.
В отличие от стека, куча не имеет ограничений по размеру переменного размера (кроме очевидных физических ограничений вашего компьютера). Память кучи немного медленнее для чтения и записи, потому что для доступа к памяти в куче нужно использовать указатели. Мы поговорим об указателях в ближайшее время.
В отличие от стека, переменные, созданные в куче, доступны любой функции в любой точке вашей программы. Переменные кучи по сути являются глобальными.
Больше можно найти здесь .
Переменные, размещенные в стеке, сохраняются непосредственно в памяти, и доступ к этой памяти очень быстрый, и его распределение решается при компиляции программы. Когда функция или метод вызывает другую функцию, которая, в свою очередь, вызывает другую функцию и т. Д., Выполнение всех этих функций приостанавливается до тех пор, пока самая последняя функция не вернет свое значение. Стек всегда резервируется в порядке LIFO, последний зарезервированный блок всегда является следующим блоком, который должен быть освобожден. Это действительно упрощает отслеживание стека, освобождение блока из стека - всего лишь настройка одного указателя.
Переменным, выделенным в куче, выделяется память во время выполнения, и доступ к этой памяти немного медленнее, но размер кучи ограничен только размером виртуальной памяти. Элементы кучи не имеют зависимостей друг от друга и всегда могут быть доступны случайным образом в любое время. Вы можете выделить блок в любое время и освободить его в любое время. Это значительно усложняет отслеживание того, какие части кучи выделены или свободны в любой момент времени.
Вы можете использовать стек, если точно знаете, сколько данных вам нужно выделить до компиляции, и он не слишком велик. Вы можете использовать кучу, если вы точно не знаете, сколько данных вам потребуется во время выполнения или если вам нужно выделить много данных.
В многопоточной ситуации каждый поток будет иметь свой собственный полностью независимый стек, но они будут совместно использовать кучу. Стек зависит от потока, а куча зависит от приложения. Стек важно учитывать при обработке исключений и выполнении потоков.
Каждый поток получает стек, в то время как для приложения обычно есть только одна куча (хотя весьма часто иметь несколько куч для разных типов размещения).
Во время выполнения, если приложению требуется больше кучи, оно может выделить память из свободной памяти, а если стеку требуется память, оно может выделить память из свободной памяти, выделенной для приложения.
Даже более подробно здесь и здесь .
Теперь перейдите к ответам на ваш вопрос .
ОС выделяет стек для каждого потока системного уровня при его создании. Обычно ОС вызывается языковой средой выполнения для выделения кучи для приложения.
Больше можно найти здесь .
Уже дано в топе.
Больше можно найти здесь .
Размер стека устанавливается ОС при создании потока. Размер кучи устанавливается при запуске приложения, но может увеличиваться по мере необходимости в пространстве (распределитель запрашивает больше памяти у операционной системы).
Распределение стека происходит намного быстрее, поскольку все, что он на самом деле делает - это перемещает указатель стека. Используя пулы памяти, вы можете получить сопоставимую производительность за счет распределения кучи, но это связано с небольшой дополнительной сложностью и собственными головными болями.
Кроме того, стек против кучи - это не только вопрос производительности; он также много говорит вам об ожидаемом времени жизни объектов.
Подробности можно найти здесь .
источник
В 1980-х годах UNIX распространялся как зайчики, а крупные компании катались самостоятельно. У Exxon был такой же, как и утерянные в истории десятки брендов. То, как память была размещена, оставалось на усмотрение многих разработчиков.
Типичная C-программа была разложена в памяти с возможностью увеличения путем изменения значения brk (). Как правило, HEAP была чуть ниже этого значения brk, а увеличение brk увеличивало количество доступной кучи.
Один STACK обычно представлял собой область ниже HEAP, которая представляла собой участок памяти, не содержащий ничего ценного, до вершины следующего фиксированного блока памяти. Этот следующий блок часто был CODE, который мог быть перезаписан стековыми данными в одном из известных хаков своей эпохи.
Одним типичным блоком памяти был BSS (блок нулевых значений), который случайно не был обнулен в предложении одного производителя. Другой был DATA, содержащий инициализированные значения, включая строки и числа. Третьим был CODE, содержащий CRT (время выполнения C), main, функции и библиотеки.
Появление виртуальной памяти в UNIX меняет многие ограничения. Нет объективной причины, по которой эти блоки должны быть смежными, фиксированными по размеру или заказанными определенным образом. Конечно, до UNIX были Multics, которые не страдали от этих ограничений. Вот схема, показывающая один из макетов памяти той эпохи.
источник
стек , куча и данные каждого процесса в виртуальной памяти:
источник
Пара центов: я думаю, будет хорошо нарисовать память графически и проще:
Стрелки - показывают, где растут стек и куча, размер стека процесса имеет ограничение, определенное в ОС, ограничения размера стека потока по параметрам в API создания потока обычно. Обычно размер кучи ограничивается максимальным размером виртуальной памяти процесса, например, для 32-разрядных 2-4 ГБ.
Таким простым способом: куча процесса является общей для процесса и всех потоков внутри, используя для распределения памяти в общем случае что-то вроде malloc () .
Стек - это быстрая память для хранения в общем случае указателей и переменных возврата функций, которые обрабатываются как параметры в вызове функции, локальные переменные функции.
источник
Так как некоторые ответы стали придирчивыми, я собираюсь внести свою лепту.
Удивительно, но никто не упомянул, что несколько (то есть не связанных с количеством запущенных потоков на уровне ОС) стеков вызовов можно найти не только в экзотических языках (PostScript) или платформах (Intel Itanium), но также в волокнах , зеленых потоках. и некоторые реализации сопрограмм .
Волокна, зеленые нити и сопрограммы во многом похожи, что приводит к путанице. Разница между волокнами и зелеными нитями заключается в том, что первые используют кооперативную многозадачность, в то время как последние могут иметь либо кооперативную, либо вытесняющую (или даже обе). Различия между волокнами и сопрограммами см. Здесь .
В любом случае, назначение обоих волокон, зеленых нитей и сопрограмм состоит в том, чтобы несколько функций выполнялись одновременно, но не параллельно (см. Этот вопрос в разделе SO ) в пределах одного потока уровня ОС, передавая управление вперед и назад друг от друга. организованно.
При использовании волокон, зеленых нитей или сопрограмм у вас обычно есть отдельный стек для каждой функции. (Технически, для каждой функции используется не просто стек, а целый контекст выполнения. Самое главное, регистры ЦП.) Для каждого потока существует столько стеков, сколько имеется одновременно выполняемых функций, и поток переключается между выполнением каждой функции. в соответствии с логикой вашей программы. Когда функция запускается до конца, ее стек уничтожается. Таким образом, количество и время жизни стеков являются динамическими и не определяются количеством потоков на уровне ОС!
Обратите внимание, что я сказал, что " обычно есть отдельный стек для каждой функции". Там вы оба stackful и Stackless реализации couroutines. Большинство реализаций отличается stackful C ++ являются Boost.Coroutine и Microsoft Закон о госзакупках «s
async/await
. (Тем не менее, возобновляемые функции C ++ (также называемые «async
иawait
»), которые были предложены для C ++ 17, скорее всего будут использовать сопрограммы без стеков.)Предложение Fibers к стандартной библиотеке C ++ ожидается. Также есть сторонние библиотеки . Зеленые темы чрезвычайно популярны в таких языках, как Python и Ruby.
источник
Мне есть чем поделиться, хотя основные моменты уже освещены.
стек
отвал
Интересная заметка:
источник
Вот Это Да! Так много ответов, и я не думаю, что один из них понял это правильно ...
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
источник
Многие ответы верны как понятия, но мы должны отметить, что аппаратному обеспечению необходим стек (то есть микропроцессор), чтобы разрешить вызов подпрограмм (CALL на ассемблере ..). (ООП ребята будут называть это методами )
В стеке вы сохраняете обратные адреса, а вызов → push / ret → pop управляется напрямую аппаратно.
Вы можете использовать стек для передачи параметров ... даже если он медленнее, чем регистры (скажет гуру микропроцессора или хорошая книга по BIOS 1980-х годов ...)
Использование стека происходит быстрее:
источник
malloc
это вызов ядра?Источник: Академинд
источник
Спасибо за действительно хорошее обсуждение, но как настоящий нуб, мне интересно, где хранятся инструкции? В НАЧАЛЕ ученые выбирали между двумя архитектурами (фон Нейманн, где все считается ДАННЫМИ и ГАРВАРД, где область памяти была зарезервирована для инструкций, а другая - для данных). В конечном счете, мы выбрали дизайн фон Неймана, и теперь все считается «одним и тем же». Это мешало мне, когда я изучал сборку https://www.cs.virginia.edu/~evans/cs216/guides/x86.html, потому что они говорят о регистрах и указателях стека.
Все выше говорит о ДАННЫХ. Я предполагаю, что поскольку инструкция - это определенная вещь с определенным объемом памяти, она будет помещаться в стек, и поэтому все «те» регистры, которые обсуждались в сборке, находятся в стеке. Конечно же, тогда появилось объектно-ориентированное программирование с инструкциями и данными, объединенными в динамическую структуру, так что теперь инструкции будут также храниться в куче?
источник