Реклама:

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

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

Вместо этого используется другая стратегия. Для переменных резервируется особая область памяти, которая называется стеком и в которой отдельные переменные не получают абсолютных адресов. Какой-либо регистр, скажем, LV, указывает на базовый адрес локальных переменных для текущей процедуры. Посмотрите на рис. 4.7, а. В данном случае вызывается процедура А с локальными переменными al, а2 и аЗ, и для этих переменных резервируется участок памяти, начинающийся с адреса, который указывается регистром LV. Другой регистр, SP, указывает на старшее слово локальных переменных процедуры А. Если значение регистра LV равно 100, а слова по 4 байт, то значение SP составляет 108. Для обращения к переменной нужно вычислить ее смещение от адреса LV. Структура данных между LV и SP (включая оба указанных слова) называется фреймом локальных переменных.

Стек

Рис. 4.7. Стек для хранения локальных переменных: во время вызова процедуры А (а); после того, как процедура А вызывает процедуру В (б); после того, как процедура В вызывает процедуру С (б); после того как процедуры С и В завершаются, а процедура А вызывает процедуру О (г)

А теперь давайте посмотрим, что произойдет, если процедура А вызывает другую процедуру, например В. Где должны храниться 4 локальные переменные процедуры В (61, 62, 63, 64)? Ответ: в стеке, расположенном над стеком для процедуры А, как показано на рис. 4.7, б. Отметим, что после вызова процедуры регистр \У указывает уже на локальные переменные процедуры В. Обращаться к локальным переменным процедуры В можно по их сдвигу от 1У. Если процедура В вызывает процедуру С, то регистры ЬУ и 8Р снова переопределяются и указывают на местонахождение локальных переменных процедуры С, как показано на рис. 4.7, в.

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

Предположим, что процедура А вызывает процедуру Д которая содержит 5 локальных переменных. Соответствующий стек показан на рис. 4.7, г. Локальные переменные процедуры В используют область памяти процедуры В и часть стека процедуры С. В памяти с такой организацией размещаются только текущие процедуры. Когда процедура завершается, отведенный для нее участок памяти освобождается.

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

а\ = а2 + аЗ.

Чтобы вычислить эту сумму, можно поместить а2 в стек, как показано на рис. 4.8, а. Тогда значение регистра 8Р увеличится на число, равное количеству байтов в слове (скажем, на 4), и будет указывать на адрес первого операнда. Затем в стек помещается переменная аЗ, как показано на рис. 4.8, б. Отметим, что названия процедур и переменных выбираются пользователем, а названия регистров и кодов операций жестко определены, поэтому названия процедур и переменных мы выделяем в тексте курсивом.

Стек

Рис. 4.8. Использование стека операндов для выполнения арифметических действий

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

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

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

Пример архитектуры набора команд - IJVM || Оглавление || Модель памяти IJVM