Почему память разбита на стек и кучу?

Возможный дубликат:
Что и где это стек и куча

У меня есть пара вопросов по поводу стека или кучи.

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

Однако мне всегда было интересно, как именно работают стек и куча. ОЗУ - это всего лишь один фрагмент памяти, он не делится на «стек» и «кучу» (или нет?). Если да, то почему мы вообще разделяем память на стек и кучу?

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

Я почти уверен, что это не так. Но почему!? Может ли кто-нибудь дать мне подробный ответ?

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


person xcrypt    schedule 17.11.2011    source источник
comment
stackoverflow.com/ questions / 7123936 /   -  person drdwilcox    schedule 17.11.2011
comment
stackoverflow.com/ вопросы / 79923 /   -  person Luc M    schedule 18.11.2011


Ответы (3)


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

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

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

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

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

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

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

person Caleb    schedule 17.11.2011
comment
Не могли бы вы привести мне пример того, где я вынужден использовать кучу? Например, я мог бы просто выделить все необходимое в стеке для всей программы в родительской функции и передать все по адресу дочерним функциям. РЕДАКТИРОВАТЬ: давайте проигнорируем, что стек ограничен чем-либо еще, кроме переполнения оперативной памяти, ради вопроса. - person xcrypt; 18.11.2011
comment
@xcrypt Для этого вам потребуется заранее знать о каждом выделении памяти, которое может выполнить ваша программа. В качестве альтернативы вы можете выделить гигантский блок в своем стеке, из которого впоследствии вы сможете динамически выделять память. Этот блок будет функциональным эквивалентом кучи. Я добавлю пример выше. - person Caleb; 18.11.2011
comment
Вы упомянули, что компилятор вычисляет необходимый ему объем стека перед запуском, верно? Верно ли это и в случае рекурсии? Потому что, если это так, не должен ли компилятор предлагать бесконечную рекурсию сразу после компиляции кода? - person Dubby; 19.07.2014
comment
@Dubby Отвечая на ваш вопрос 3 года спустя ... компилятор может посмотреть на функцию и выяснить, сколько локального хранилища ему нужно, но в целом он не может знать, сколько хранилища потребуется другим функциям, которые вызывает функция, включая рекурсивные вызовы. Решения о том, какие функции вызывать и когда происходят во время выполнения в ответ на данные, которые компилятор не может знать, поэтому он не может знать, сколько общего пространства стека будет использовано, как долго будет продолжаться рекурсивный процесс и т. Д. - person Caleb; 08.09.2017

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

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

И куча не имеет четко определенного значения (кроме «динамически выделяемой памяти, которой нет в стеке»). На самом деле, в системах Linux выделение большого количества памяти с помощью системного вызова mmap является справедливым быстро (но malloc реализации стараются избегать mmap и предпочитают повторно использовать free-d память). Проблема заключается в выделении небольших зон памяти.

И узнайте больше о методах сбора мусора. В C или C ++ вы можете использовать GC Boehm

Стек часто бывает полезен, особенно для рекурсивных вызовов функций. Это настолько полезно (например, в C), что современные процессоры обычно имеют специальный регистр указателя стека (используемый машинными инструкциями CALL & RET для вызова и возврата). Но так было не всегда; на некоторых процессорах (например, IBM360) указатель стека является обычным регистром, а не жестко запрограммированным.

См. Также это и это ответы (и другие) о виртуальном адресном пространстве.

person Basile Starynkevitch    schedule 17.11.2011
comment
очень полезная информация, спасибо :) - person xcrypt; 18.11.2011
comment
Не могли бы вы пояснить, почему невозможно использовать только стек? Интуитивно это верно, потому что только с одним стеком мы получили автоматы выталкивания, что не эквивалентно машине Тьюринга. Но что, если язык поддерживает только чистые функции (например, Haskell) и не использует ссылки (т.е. все типы значений)? Все либо передается как аргументы, либо возвращается как результат. Можно ли реализовать этот язык только с помощью стека? Может ли этот язык быть полным по Тьюрингу? - person wlnirvana; 10.12.2019

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

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

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

person Gabriel Belingueres    schedule 17.11.2011
comment
Ну, я мог бы просто выделить какой-то объект в стеке в основной функции и использовать его во всей программе, мне для этого не нужна куча. Ваш аргумент может заключаться в том, что стек ограничен, но одна из вещей, которые я хотел задать своими вопросами, была: почему стек ограничен? (по причинам, указанным выше) - person xcrypt; 18.11.2011
comment
Стек ограничен, потому что он увеличивается от одного крайнего значения адреса пространства памяти до другого. Если бы оно было неограниченным, вы можете повредить дату, хранящуюся в куче, когда присутствует прерывание (потому что прерывание сохраняет состояние ЦП в стеке), поэтому где-то должно быть установлено искусственное ограничение, чтобы этого избежать (и это потому, что существует известное сообщение о переполнении стека при достижении этого предела). - person Gabriel Belingueres; 19.11.2011