2015-04-30 1 views
12

Я читал What and where are the stack and heap?. Одна вещь, которую я немного нечеткой, - это то, что происходит со стеком после выхода метода. Возьмите это изображение, например:Что происходит со стеком при выходе из метода?

Stack

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

+1

https://docs.oracle.com /javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6 – fabian

+0

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

+0

Ознакомьтесь с этой 17-летней статьей о том, как виртуальная машина Java обрабатывает вызов и возврат метода] (http://www.javaworld.com/article/2076949/learn-java/how-the-java-virtual-machine- ручки-метод-вызов-и-return.html). В нем подробно объясняется, что происходит при вызове и возврате метода. Основы JVM не должны сильно меняться в течение всего времени. Вы можете перекрестно проверить с помощью указанной выше спецификации, если у вас есть время ... – MicSim

ответ

1

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

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

Когда вызов функции завершен, указатель стека будет уменьшен на это смещение, это освободит выделенное пространство.

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

BTW: вот почему Java хранит все объекты в куче. Когда объект будет находиться в стеке, и вы вернете ссылку, указывающую на стек, объект может быть уничтожен при следующем вызове функции.

1

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

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

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

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

0

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

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

Для иллюстрации: если func1 называется func2 называется FUNC3 стека будет выглядеть примерно так:

FUNC1 арг/местного Пера ... | func2 args/local vars ... | func3 args/local vars ...

После FUNC3 возвращает будет:

FUNC1 Args/местные вары ... | func2 args/local vars ...

0

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

Если у вас есть экземпляр объектов в стеке, то есть MyClass x, а не MyClass * x = new MyClass(), тогда объект x будет разорван и его деструктор вызывается, когда стек перематывается на предыдущий кадр, который по существу просто делает текущий указатель стека (внутренний) указывать на предыдущий кадр. На большинстве родных языков память не будет очищена и т. Д.

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

1

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

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

В Java ссылки на объекты находятся в стеке, когда сам объект находится в куче. Если все ссылки на объект удаляются из стека, сборщик мусора удалит объект из кучи.

Надеюсь, мой ответ вам поможет. Кроме того, check this.

1

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

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

call the_method 

Когда это произойдет, текущий указатель команд помещается в стек. Указатель стека указывает на него. Теперь мы находимся в функции:

the_method: 
    push ebp 
    mov ebp, esp 

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

sub esp, 8 

Далее, 8 байтов (при условии выделения двух четырехбайтовых целых чисел) выделяются в стеке.

mov [ebp-4], 4 
    mov [ebp-8], 2 

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

mov esp, ebp 
    pop ebp 
    ret 

Когда это происходит, указатель стеки находится прямо туда, где он был, когда мы начинали, указывая на хранимый указатель базы (сохраненный указатель кадра). Это возвращается в EBP, оставляя ESP, указывая на указатель возврата, который затем «выталкивается» в EIP с помощью ret. Фактически, стек разматывается. Несмотря на то, что фактические места памяти не изменились для двух локальных переменных, они эффективно выше стека (физически ниже в памяти, но я думаю, что вы понимаете, что я имею в виду.)

Смежные вопросы