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

avatar
mattshane
17 сентября 2008 в 04:18
1638333
30
8638

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

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

действительно хорошее объяснение можно найти здесь В чем разница между стеком и кучей?

Ben
15 февраля 2014 в 05:50
14

Также (действительно) хорошо: codeproject.com/Articles/76153/… (часть стека / кучи)

Selvamani
11 июня 2016 в 05:42
14

youtube.com/watch?v=clOUdVDDzIM&spfreload=5

jww
21 июня 2017 в 16:23
4

См. Также Stack Clash. Исправления Stack Clash затронули некоторые аспекты системных переменных и поведения, например rlimit_stack. Также см. Red Hat Issue 1463241

Matthew
12 ноября 2017 в 22:38
3

@mattshane. Определения стека и кучи вообще не зависят от типов значений и ссылок. Другими словами, стек и куча могут быть полностью определены, даже если типы значений и ссылочные типы никогда не существовали. Кроме того, для понимания типов значений и ссылок стек - это просто деталь реализации. Эрик Липперт: Стек - это деталь реализации, часть первая.

mins
5 июля 2019 в 07:20
1

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

user2846495
4 мая 2020 в 14:04
0

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

Ответы (30)

avatar
Jeff Hill
17 сентября 2008 в 04:52
6365

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

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

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

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

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

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

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

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

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

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

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

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

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

sqykly
8 октября 2013 в 08:31
104

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

Sina Madani
15 августа 2016 в 19:06
371

Меня очень смущает диаграмма в конце. Я думал, что понял, пока не увидел это изображение.

sqykly
13 октября 2016 в 15:06
10

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

Jatin Shashoo
22 июля 2018 в 06:22
3

«стек - это память, отведенная под временное пространство». Прохладный. Но где это собственно «отложено» в терминах структуры памяти Java ?? Это память кучи / память без кучи / другое (структура памяти Java согласно betsol.com/2017/06/…)

kbec
6 сентября 2018 в 15:41
5

@JatinShashoo Среда выполнения Java, как интерпретатор байт-кода, добавляет еще один уровень виртуализации, так что то, что вы упомянули, является всего лишь точкой зрения приложения Java. С точки зрения операционной системы, это всего лишь куча, в которой процесс времени выполнения Java выделяет часть своего пространства как «не-кучную» память для обрабатываемого байт-кода. Остальная часть этой кучи уровня ОС используется как куча уровня приложения, где хранятся данные объекта.

kbec
6 сентября 2018 в 15:47
0

Итак, стек - это «отложенная» куча в терминах уровня ОС. Стек управляется непосредственно потоком с использованием инструкций ЦП, а куча - это область обычной памяти, отображаемая ОС.

Z80
26 апреля 2019 в 08:19
1

Здравствуй. Я думаю, что в вашем сообщении (что в остальном здорово) четко не говорится, что куча медленнее, чем стек, только когда вы СОЗДАЕТЕ материал (объекты). Когда вы ДОСТУПНЫ к материалу, куча должна иметь примерно такую ​​же скорость, что и стек.

Jacob Pritchett
13 мая 2019 в 15:01
1

@sqykly Было бы правильно называть старые игровые ОС сами по себе? У них есть своего рода графический интерфейс, они управляют низкоуровневым вводом-выводом и имеют полный доступ ко всему аппаратному обеспечению, а также работают как процесс «корневого уровня». Именно по этим причинам я всегда видел их такими, и даже на таких новых консолях, как Wii, на самом деле запускалось только одно: меню HOME было встроено в каждую игру, а не предоставлялось "основным" ОС »(главное меню), которая вообще не работает в фоновом режиме. За исключением новых консолей, большинство старых игр, кажется, исполняют роль ОС.

Peregring-lk
13 августа 2019 в 19:18
0

@SinaMadani, хахаха, и здесь.

Pouriya
27 августа 2019 в 18:29
1

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

user7075574
16 ноября 2019 в 01:25
0

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

catleeball
30 августа 2020 в 23:10
0

Еще несколько примечаний, которые могут иметь отношение к читателям: 1 . блок управления памятью (MMU) сопоставляет адреса физической памяти в таблице страниц с виртуальными адресами. 2 . Стек полезен для поддержания непрерывной локализации памяти, где данные в куче могут быть распределены между несколькими устройствами памяти, такими как ОЗУ, кэш L1, кеш L2 и т. Д. 3 рассмотрите возможность хранения фиксированного массива из 10 элементов, каждая из них находится рядом в памяти в стеке, итерация / поиск элемента тривиальны. Для 10 элементов в связанном списке в куче может потребоваться поиск не только в другом блоке памяти, но и в разных устройствах памяти.

unqualified
21 ноября 2020 в 18:56
0

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

Danny
31 декабря 2020 в 13:22
2

На диаграмме видно, что локальные переменные в стеке ссылаются только на объекты в куче. Возможно ли, чтобы локальные переменные также ссылались на объекты в стеке? Если я, например, делаю int x = 5; внутри вызова функции в C, это 5 не хранится в стеке?

avatar
A. Hendry
20 ноября 2021 в 22:17
0

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

ОТВЕТ: Оба находятся в оперативной памяти.

В стороне:

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

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

Какова их область применения?
Что определяет размер каждого из них?
Что делает человека быстрее?

ОТВЕТ:

  1. Стек предназначен для статических данных (фиксированного размера)

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

    i. Для этих переменных выделяется фиксированный объем памяти.
    II. Этот размер этой памяти не может увеличиваться.

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

    c. Объект, помещенный в стек, который во время выполнения увеличивается в памяти, превышая размер стека, вызывает ошибку переполнения стека

  2. Куча предназначена для динамических (изменяющихся размеров) данных

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

    .

    б. Поскольку элементы распределяются в куче путем поиска свободного места везде, где оно существует в ОЗУ, данные не всегда находятся в непрерывном разделе, что иногда делает доступ медленнее, чем стек

    c. Программисты вручную помещают элементы в стек с помощью ключевого слова new и ДОЛЖНЫ вручную освобождать эту память после завершения ее использования.
    я. Код, который повторно выделяет новую память без ее освобождения, когда она больше не нужна, из-за утечки памяти.

В стороне:

Стек и куча были введены не для повышения скорости; они были введены для обработки переполнения памяти. Первая проблема, связанная с использованием стека по сравнению с кучей, должна заключаться в том, не произойдет ли переполнение памяти. Если объект предназначен для увеличения в размере до неизвестной величины (например, связанный список или объект, элементы которого могут содержать произвольный объем данных), поместите его в кучу. Насколько это возможно, используйте контейнеры стандартной библиотеки (STL) C++ vector, map и list, поскольку жизнь проще (вам не нужно беспокоиться о выделении/освобождении памяти).

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

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

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

ОТВЕТ:

  • Размер стека определяется компилятором во время компиляции.

  • Размер кучи изменяется во время выполнения. (Куча работает с ОС во время выполнения для выделения памяти.)

В стороне:

Ниже немного больше об управлении и операциях времени компиляции и времени выполнения.

Каждый компьютер имеет уникальную архитектуру набора инструкций (ISA), которая является его аппаратными командами (например, "MOVE", "JUMP", "ADD" и т. д.).

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

  • ISA ОС называется голой машиной, а остальные команды называются расширенной машиной. ядро — это первый уровень расширенной машины. Он управляет такими вещами, как

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

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

avatar
anshkun
11 октября 2021 в 08:20
0

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

Когда требуется больше кучи, ОС будет выделять память динамически, и фрагмент кучи всегда практически непрерывен

См. Системные вызовы brk(), sbrk() и alloca() в linux

avatar
Olivier Rogier
5 июля 2020 в 10:50
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

avatar
aquagremlin
9 апреля 2020 в 15:29
0

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

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

Sold Out
2 июля 2020 в 11:07
0

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

avatar
nCardot
22 марта 2020 в 03:16
2

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

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

Источник: Academind

avatar
ar18
20 февраля 2019 в 02:04
16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ССЫЛКА:

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

avatar
pkthapa
15 ноября 2017 в 18:27
25

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

Стек

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

Колда

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

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

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

лаконично и чисто. отлично:)

avatar
Yousha Aleayoub
14 сентября 2017 в 17:32
42

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

stack, heap and static data

avatar
davec
30 июля 2017 в 12:13
207

Чтобы уточнить, в этом ответе неверная информация (Томас исправил свой ответ после комментариев, круто :)). В других ответах просто не объясняется, что означает статическое распределение. Итак, я объясню три основных формы распределения и то, как они обычно связаны с кучей, стеком и сегментом данных ниже. Я также покажу несколько примеров как на 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 в приведенном выше примере кода. Такие переменные могут сбить с толку наши обычные, но неформальные привычки именования. Например, когда мы говорим « локальная », мы обычно имеем в виду « локально автоматически выделяемая переменная », а когда мы говорим «глобальная», мы обычно имеем в виду « статически распределенная переменная с глобальной областью действия <63200615 ". К сожалению, когда дело доходит до таких вещей, как " статически распределенные переменные с областью видимости файла ", многие люди просто говорят ... " да ??? ".

Некоторые варианты синтаксиса в 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
supercat
9 декабря 2013 в 21:53
1

Я бы назвал статическую переменную, объявленную в функции, имеющей только локальную доступностью , но обычно не использовал бы для нее термин «область действия». Кроме того, стоит отметить, что один аспект стека / кучи, с которым языки имеют практически нулевую гибкость: язык, который сохраняет контекст выполнения в стеке, не может использовать тот же стек для хранения вещей, которые должны будут пережить контексты, в которых они созданы . Некоторые языки, такие как PostScript, имеют несколько стеков, но имеют «кучу», которая больше похожа на стек.

davec
17 декабря 2013 в 20:28
0

@supercat Все это имеет смысл. Я определил область видимости как «какие части кода могут обращаться к переменной» (и считаю, что это наиболее стандартное определение), поэтому я думаю, что мы согласны :)

supercat
17 декабря 2013 в 20:57
0

Я бы рассматривал «объем» переменной как ограниченный не только пространством, но и временем. Переменная в области класса-объекта должна сохранять свое значение, пока существует объект. Переменная в области контекста выполнения должна сохранять свое значение, пока выполнение остается в этом контексте. Объявление статической переменной создает идентификатор , область действия которого ограничена текущим блоком, который присоединяется к переменной , область действия которой неограничена.

davec
28 декабря 2013 в 22:50
0

@supercat Вот почему я использую слово «время жизни», как я называю то, что вы называете временной областью. Это уменьшает необходимость перегружать слово «объем» таким количеством значений. Насколько я могу судить, похоже, что нет единого мнения относительно точных определений, даже среди канонических источников. Моя терминология частично заимствована из K&R, а частично из преобладающего использования на первом отделении CS, на котором я учился / преподавал. Всегда приятно услышать еще одно информированное мнение.

supercat
29 декабря 2013 в 01:35
0

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

supercat
29 декабря 2013 в 01:36
0

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

Zaeem Sattar
16 мая 2017 в 09:57
1

ты, наверное, шутишь. вы действительно можете определить статическую переменную внутри функции?

davec
22 июня 2017 в 22:41
1

@zaeemsattar абсолютно, и это не редкость, чтобы увидеть в коде C

Tom Leys
26 июля 2018 в 22:42
1

@ZaeemSattar Думайте о статической переменной функции как о скрытом глобальном или как о частной статической переменной-члене.

avatar
ingconti
27 июля 2017 в 22:14
10

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

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

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

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

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

  • Это аппаратное обеспечение, и даже push / pop очень эффективны.
  • malloc требует входа в режим ядра, использования блокировки / семафора (или других примитивов синхронизации), выполнения некоторого кода и управления некоторыми структурами, необходимыми для отслеживания распределения.
Peter Mortensen
30 июля 2017 в 12:34
0

Что такое OPP? Вы имеете в виду ООП (объектно-ориентированное_программирование)?

Peter Mortensen
30 июля 2017 в 12:35
0

Вы хотите сказать, что malloc - это вызов ядра?

ingconti
3 августа 2017 в 12:18
0

1) да, извините ... ООП ... 2) malloc: я пишу вкратце, извините ... malloc находится в пользовательском пространстве ... но может запускать другие вызовы ... дело в том, что использование кучи МОЖЕТ быть очень медленным ...

mins
5 июля 2019 в 07:48
0

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

ingconti
19 июля 2020 в 06:24
0

«NET thread» - это не настоящий стек. (то же самое для JVM): это концепции SW. (другие называют это «активационной записью») Мы должны начать с реальных схем, как это было в истории ПК, чтобы получить реальное понимание.

avatar
Alireza
18 июля 2017 в 15:04
51

Хорошо, просто и коротко, они означают заказанный и не заказанный ...!

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

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

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

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

enter image description here

avatar
Abrar Jahin
2 мая 2016 в 12:16
53

Вкратце

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


Подробнее

Стек

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

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

Больше можно найти здесь .


Куча

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

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

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

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

Больше можно найти здесь .


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

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

Enter image description here

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

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

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

Enter image description here

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

Даже более подробная информация дана здесь и здесь <29>.


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

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

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

Больше можно найти здесь .

Какова их сфера действия?

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

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

Больше можно найти в здесь.

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

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

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

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

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

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

avatar
Maxim Akristiniy
17 декабря 2015 в 15:08
28

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

This is my vision of process memory construction with simplification for more easy understanding wht happening


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

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

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

avatar
jlettvin
27 марта 2015 в 19:55
37

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

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

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

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

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

A typical 1980s style UNIX C program memory layout

avatar
shakurov
2 марта 2015 в 01:29
24

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

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

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

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

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

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

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

avatar
Shreyos Adikari
11 июня 2014 в 19:42
122

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

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

Enter image description here

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

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

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

Enter image description here

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

Оба вместе

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

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

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

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

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

Enter image description here

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

avatar
unknown
30 января 2014 в 06:33
57

Стек

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

Колда

  • Переменные доступны глобально
  • Нет ограничений на размер памяти
  • (относительно) более медленный доступ
  • Нет гарантированного эффективного использования пространства, память со временем может фрагментироваться, поскольку блоки памяти выделяются, а затем освобождаются
  • Вы должны управлять памятью (вы отвечаете за выделение и освобождение переменных)
  • Размер переменных можно изменить с помощью realloc ()
avatar
Snowcrash
9 ноября 2012 в 12:28
423

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

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

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

Picture of variables on the stack

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

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

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

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

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

и здесь:

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

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

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

davec
10 ноября 2012 в 23:05
16

Это неверно. i и cls не являются «статическими» переменными. они называются «локальными» или «автоматическими» переменными. Это очень важное различие. См. [Ссылку] coderhelper.com/a/13326916/1763801 для уточнения.

Snowcrash
20 ноября 2012 в 14:38
9

Я не сказал, что это статические переменные . Я сказал, что int и cls1 - статические элементы . Их память выделяется статически, и поэтому они отправляются в стек. В этом отличие от объекта, которому требуется динамическое выделение памяти, которое, следовательно, хранится в куче.

davec
21 ноября 2012 в 16:55
12

Цитирую «Статические предметы ... уходят в стопку». Это просто неправильно. Статические элементы помещаются в сегмент данных, автоматические элементы помещаются в стек.

davec
21 ноября 2012 в 17:02
14

Также тот, кто написал эту статью о кодпроекте, не знает, о чем он говорит. Например, он говорит, что «примитивным нужна память статического типа», что совершенно неверно. Ничто не мешает вам динамически размещать примитивы в куче, просто напишите что-нибудь вроде «int array [] = new int [num]» и вуаля, примитивы размещаются динамически в .NET. Это лишь одна из нескольких неточностей.

davec
21 ноября 2012 в 17:04
1

Если вы посмотрите мою репутацию, то увидите, что я еще не могу проголосовать против. Так что это даже не я голосую против.

sree
2 апреля 2014 в 14:51
0

@Snow Crash, в вашем ответе в начале ссылка This helps: не работает. Пожалуйста, исправьте это. Спасибо.

Vagish
9 июня 2014 в 08:24
1

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

Tom Leys
9 сентября 2014 в 23:09
8

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

confused00
16 января 2015 в 12:53
1

@SnowCrash один вопрос о вашем изображении - как мне получить доступ к i после выделения y? Должен ли я выскакивать y? Поменять их местами? Что, если их разделяет множество локальных переменных?

Koray Tugay
3 мая 2015 в 09:29
0

@ confused00 У вас еще есть ответ на этот вопрос? Я думаю, что все они хранятся в массиве, подобном структуре, и во время компиляции они помещаются в стек операндов, верно?

Lightness Races in Orbit
13 января 2020 в 16:23
0

@ confused00 Ни то, ни другое - потому что это не стеки . Это фреймы стека вызовов. Каждый кадр можно просто катать как угодно. Тогда еще одно неточное / вводящее в заблуждение описание!

avatar
Martin Liversage
31 июля 2009 в 15:54
765

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

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

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

Куча

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

The heap

Стек

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

The stack

Может ли функция быть размещена в куче вместо стека?

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

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

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

Bikal Lem
25 апреля 2012 в 16:42
38

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

Nps
15 февраля 2014 в 10:26
3

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

Alex
19 марта 2014 в 19:59
20

Этот ответ, на мой взгляд, был лучшим, потому что он помог мне понять, что на самом деле представляет собой оператор return и как он соотносится с этим «адресом возврата», с которым я время от времени сталкиваюсь, что означает поместить функцию в стек, и почему функции помещаются в стеки. Отличный ответ!

Qix - MONICA WAS MISTREATED
29 ноября 2014 в 02:00
3

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

Koray Tugay
3 мая 2015 в 09:36
2

Что вы имеете в виду: «Код функции может перемещаться вверх по стеку от текущего указателя стека, чтобы найти эти значения». ? Не могли бы вы подробнее рассказать об этом?

Martin Liversage
3 мая 2015 в 10:59
1

@KorayTugay: когда функция начинает выполнение, вершина стека (самый низкий адрес, потому что стек «инвертирован») содержит адрес возврата ( ret ). Аргументы функции ( args ) находятся ниже этого в стеке (по более высокому адресу). Объявление аргументов функции определяет расположение аргументов в стеке, и код функции может получить доступ к этим аргументам, зная только указатель стека при вводе функции, а также точный размер и последовательность аргументов. Все относительно указателя стека.

Koray Tugay
3 мая 2015 в 14:30
0

@MartinLiversage Я думал, что к ним обращаются по индексам массива.

Martin Liversage
3 мая 2015 в 14:37
0

@KorayTugay: Мое объяснение касается уровня ЦП / машинного кода / сборки, где есть только регистры и память, которые можно адресовать, чтобы иметь возможность читать и записывать данные. В общем, массивы представляют собой концепцию более высокого уровня (языка программирования). Хорошо, у ЦП есть векторные инструкции, но они не важны, когда вы пытаетесь понять стек.

Koray Tugay
3 мая 2015 в 14:41
0

@MartinLiversage Хорошо, спасибо за разъяснения. Я пытаюсь создать простой интерпретатор для языка (для учебных целей), поэтому я запутался.

Francis Cugler
6 мая 2015 в 01:53
0

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

Francis Cugler
6 мая 2015 в 01:54
0

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

Sina Madani
15 августа 2016 в 19:24
1

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

Hakim
14 ноября 2018 в 10:50
0

Что означает «адрес возврата» функции в записях активации в стеке

Martin Liversage
14 ноября 2018 в 11:09
0

@Hakim: адрес возврата - это текущий указатель инструкции перед вызовом. Я пытаюсь объяснить это в третьем пункте в разделе Stack . Перед вызовом функции текущий указатель инструкции помещается в стек. Когда функция возвращает этот адрес, он извлекается из стека, и выполнение возобновляется со следующей инструкции. Запись активации вызова функции содержит адрес возврата, по которому ЦП должен продолжить выполнение после завершения функции. Функция этого не знает. Он должен быть предоставлен вызывающей функцией.

Hakim
14 ноября 2018 в 11:35
0

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

Hakim
14 ноября 2018 в 11:45
0

@MartinLiversage: Итак, стек вызовов фактически сохраняется в стеке через различные записи активации, каждая из которых представляет функцию, не так ли?

Martin Liversage
14 ноября 2018 в 13:39
0

@Hakim: Стек вызовов - это зарезервированная память, используемая для реализации вызовов функций. Каждый вызов создает новую запись активации в стеке. Запись активации содержит 1) аргументы, предоставленные функции, 2) где выполнение будет продолжено после завершения функции, и 3) локальные переменные функции. Записи активации накладываются друг на друга (отсюда и название stack ), что позволяет многократно вызывать одну и ту же функцию (рекурсия). Когда функция возвращает, ее запись активации отбрасывается, а вызывающая функция и ее запись активации становятся активными. См. Диаграмму 2.

avatar
devXen
2 апреля 2009 в 01:25
84

Из WikiAnwser.

Стек

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

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

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

Куча

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

Lightness Races in Orbit
25 апреля 2017 в 09:02
0

«Мне больше нравится принятый ответ, потому что он еще более низкий». Это плохо, а не хорошо.

avatar
Peter
19 марта 2009 в 15:55
94

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

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

Peter Mortensen
30 июля 2017 в 12:00
0

Насколько портативна alloca? Например, работает ли он в Windows? Только для Unix-подобных операционных систем?

avatar
T.E.D.
19 марта 2009 в 15:13
92

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

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

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

OmarOthman
12 февраля 2012 в 06:36
0

[@ T.E.D.] Почему вы сказали «иногда параметры помещаются в стек»? Я знаю, что они всегда . Не могли бы вы подробнее рассказать?

T.E.D.
3 апреля 2012 в 15:57
1

@OmarOthman - я говорю это, потому что это полностью зависит от автора вашего компилятора / интерпретатора, что происходит при вызове подпрограммы. Классическое поведение Fortran - вообще не использовать стек. Некоторые языки поддерживают такие экзотические вещи, как передача по имени, которая фактически является текстовой заменой.

avatar
thomasrutter
19 марта 2009 в 14:38
1422

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

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

    Stack like a stack of papers

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

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

    Heap like a heap of licorice allsorts

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

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

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

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

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

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

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

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

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

    >

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

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

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

David Conrad
10 августа 2012 в 16:00
0

Неправильный образ стопки; это должно быть что-то вроде thermo-box.co.uk/images/stories/FiniW/…, поэтому его также называют «выталкивающим стеком».

thomasrutter
13 августа 2012 в 03:40
21

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

davec
10 ноября 2012 в 23:07
10

В этом ответе есть большая ошибка. Статические переменные не размещаются в стеке. См. Мой ответ [ссылка] coderhelper.com/a/13326916/1763801 для уточнения. вы приравниваете "автоматические" переменные к "статическим" переменным, но это совсем не одно и то же

davec
11 ноября 2012 в 01:44
15

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

thomasrutter
12 ноября 2012 в 00:26
1

Когда я сказал «статически распределенные» локальные переменные, я имел в виду в отличие от динамически распределенных. Если вы запускаете функцию с int a;, a попадет в стек. Если вы запустите функцию с int *a = (int *) malloc(32768*sizeof(int));, то содержимое этого массива не будет в стеке, поскольку оно выделяется динамически.

thomasrutter
12 ноября 2012 в 00:29
10

Я только что понял, что вы правы - в C статическое распределение - это отдельная вещь, а не термин для всего, что не динамическое . Я отредактировал свой ответ, спасибо.

davec
12 ноября 2012 в 17:16
5

Это не только C. Java, Pascal, Python и многие другие имеют понятия статического, автоматического и динамического выделения. Выражение «статическое распределение» означает почти везде одно и то же. Ни на одном языке статическое распределение не означает «не динамическое». Вам нужен термин «автоматическое» распределение для того, что вы описываете (то есть для вещей в стеке).

davec
12 ноября 2012 в 18:13
0

и если вы хотите увидеть это в действии на python вместо c, я добавил несколько примеров кода ниже :)

Timeless
17 ноября 2013 в 12:07
0

все ли воспоминания спроектированы таким образом, со стеком и кучей?

thomasrutter
17 ноября 2013 в 23:03
2

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

Timeless
18 ноября 2013 в 04:32
0

@thomasrutter спасибо, а кто решил, как мы должны распределять память таким образом? конструкторы оборудования?

thomasrutter
18 ноября 2013 в 05:57
1

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

CygnusX1
10 июля 2016 в 08:39
1

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

theJerm
4 марта 2020 в 23:06
0

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

avatar
bk1e
17 сентября 2008 в 07:16
142

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

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

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

avatar
Heath
17 сентября 2008 в 04:57
119

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

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

johne
1 сентября 2009 в 05:03
8

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

OmarOthman
12 февраля 2012 в 06:34
0

[@Heath] У меня есть небольшой комментарий к вашему ответу. Взгляните на принятый ответ на этот вопрос. В нем говорится, что бесплатный магазин , скорее всего, такой же, как куча , хотя и не обязательно.

avatar
Don Neufeld
17 сентября 2008 в 04:48
175

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

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

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

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

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}
Peter Mortensen
30 июля 2017 в 11:19
0

Re "в отличие от alloc": Вы имеете в виду "в отличие от malloc"?

Peter Mortensen
30 июля 2017 в 11:20
0

Насколько портативен alloca?

Don Neufeld
1 ноября 2017 в 17:38
0

@PeterMortensen, это не POSIX, переносимость не гарантируется.

avatar
Daniel Papasian
17 сентября 2008 в 04:29
123

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

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

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

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

Tom Leys
17 сентября 2008 в 04:37
2

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

avatar
Tom Leys
17 сентября 2008 в 04:27
216

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

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

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

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

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

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

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

Greg Hewgill
17 сентября 2008 в 04:31
17

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

Tom Leys
17 сентября 2008 в 04:35
2

Если вы можете использовать стек или кучу, используйте стек. Если вы не можете использовать стек, выбора действительно нет. Я часто использую и то, и другое, и, конечно же, std :: vector или аналогичный попадает в кучу. Новичку лучше избегать кучи, потому что стек - это просто!

BenPen
11 октября 2016 в 19:10
0

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

LintfordPickle
25 июля 2018 в 09:34
1

«Вот почему следует избегать кучи (хотя она все еще часто используется)». Я не уверен, что это означает на практике, тем более что память на многих языках высокого уровня управляется по-разному. Поскольку этот вопрос помечен как независимый от языка, я бы сказал, что этот конкретный комментарий / строка неуместны и неприменимы.

Tom Leys
26 июля 2018 в 22:22
2

Хорошее замечание @JonnoHampson - хотя вы и говорите правильно, я бы сказал, что если вы работаете на "языке высокого уровня" с GC, вам, вероятно, вообще не нужны механизмы распределения памяти - и поэтому не даже неважно, что такое стек и куча.

avatar
Brian R. Bondy
17 сентября 2008 в 04:20
2487

Стек :

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

Колда :

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

Пример :

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

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

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
Andy
18 марта 2009 в 22:48
34

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

johne
1 сентября 2009 в 04:37
40

Распространенное заблуждение, что язык 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

Howiecamp
8 февраля 2010 в 04:56
58

@Brian Вы должны объяснить , почему buffer [] и указатель pBuffer создаются в стеке и почему данные pBuffer создаются в куче. Я думаю, что некоторые люди могут быть сбиты с толку вашим ответом, поскольку они могут подумать, что программа специально указывает, что память выделяется в стеке, а не в куче, но это не так. Это потому, что Buffer - это тип значения, тогда как pBuffer - ссылочный тип?

Brian R. Bondy
5 апреля 2010 в 11:44
1

@Hershi: Re 1. Это уже покрыто: переменные, созданные в стеке, выйдут из области видимости и автоматически освободятся. По поводу 2. Это уже рассмотрено: Переменные в куче должны быть уничтожены вручную и никогда не выпадают из области видимости. Данные освобождаются с помощью delete, delete [] или free

Laz
25 марта 2013 в 08:22
40

«Ответственный за утечки памяти» - Heaps не несут ответственности за утечки памяти! Ленивые / забывчивые / бывшие java-кодеры / кодеры, которым наплевать, - это!

Orion Edwards
1 сентября 2014 в 02:12
5

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

Vineeth Chitteti
24 декабря 2014 в 19:03
2

Прочтите другой ответ «Компьютерная память - это просто набор адресов;« куча »и« стек »- изобретения компилятора». это правда?

Björn Hallström
28 июля 2015 в 21:13
0

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

user4418808
23 августа 2015 в 07:48
0

@ BrianR.Bondy Привет, я сомневаюсь, что, если я выделил память в куче и не освободил ее. Так будет ли эта память освобождена после завершения программы?

nbro
15 марта 2016 в 11:24
2

Итак, стеки и кучи - это абстракции, очевидно, созданные с использованием реальных структур данных. Но вам не хватает одного момента: кто создает и отвечает за жизненный цикл стека и кучи? Это операционная система? И как именно создаются эти стек и куча? Если вы не хотите включать эту информацию непосредственно в свой ответ, не могли бы вы дать ссылку на книгу или источник, где это подробно объясняется? Кроме того, ваши перечисления могли бы быть лучше, а точки перечислений можно было бы упорядочить более логичным способом!

Spikatrix
22 апреля 2016 в 10:19
1

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

user1789573
1 августа 2016 в 15:32
1

Сборщик мусора НЕ управляет стеками. Поэтому любое выделение или освобождение памяти должно выполняться вручную.

SlySven
22 сентября 2016 в 22:20
0

Один незначительный / особый случай, регистрирует в процессоре - для компилятора C / C ++, если обнаружено, что переменная "стека" имеет очень короткий срок службы или код может быть оптимизирован надлежащим образом, переменная может никогда не попасть в то, что пользователь может представить себе как «память», но держать его на самом деле под рукой к процессору в его собственном небольшом количестве рабочих регистров. Это часто имеет место для значений, используемых для «индексации» в массиве вещей, потому что, особенно для процессоров со сложным набором инструкций, они имеют быстрые / эффективные операционные коды, которые используют эти регистры для этого типа операции!