Нажатие переменных в стек и переменные, живущие в разности стеков?
Итак, я знаю, что существует 2 области памяти: Стек и Куча.
Я также знаю, что если вы создадите локальную переменную, она будет жить в стеке, а не в куче. Стек будет расти по мере ввода данных в него, как в:
![enter image description here]()
Теперь я попытаюсь передать путаницу, которую я вам даю:
Например, этот простой код Java:
public class TestClass {
public static void main(String[] args) {
Object foo = null;
Object bar = null;
}
}
преобразуется в этот байтовый код:
public static void main(java.lang.String[]);
Code:
Stack=1, Locals=3, Args_size=1
0: aconst_null
1: astore_1
2: aconst_null
3: astore_2
4: return
LineNumberTable:
line 5: 0
line 6: 2
line 7: 4
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 args [Ljava/lang/String;
2 3 1 foo Ljava/lang/Object;
4 1 2 bar Ljava/lang/Object;
где по определению acons_null:
push a null reference onto the stack
и astore_1:
store a reference into local variable 1
Смятение, которое я испытываю, это то, что мы вложили foo в стек, и мы снова сохранили его в стеке? Что означает сохранение ссылки в локальной переменной? Где живет эта локальная переменная? В том же стеке мы ввели foo или эти отдельные стеки?
Теперь, когда я вызываю метод на первом объекте, который я вставлял в стек, поскольку указатель стека указывает на последний элемент, который я нажал, как он будет обрабатываться?
Ответы
Ответ 1
В JVM существует один стек на поток. Каждый стек состоит из нескольких фреймов: каждый вызов метода создает новый фрейм, и когда вызов метода выполняется, кадр уничтожается.
В рамке стека есть две области:
- стека операндов (не путайте слово "стоп" здесь с самим стекем JVM - стек здесь обозначает область как структуру "последний в первом" ).
- Массив локальных переменных, где каждая переменная имеет индекс (начиная с нуля).
В зависимости от реализации JVM они могут быть или не быть смежными в памяти. Логически они представляют собой два отдельных раздела фрейма стека.
Как объяснено в описании aconst_null
, команда aconst_null
нажимает ссылку на объект null
на стек .
И как объяснено в описании astore_<n>
(где n
может быть 0, 1, 2 или 3):
<n>
должен быть индексом в локальном массиве переменных текущего кадра (п. 2.6). objectref
в верхней части стека операндов должен иметь тип returnAddress
или тип reference
. Он выставляется из стека операндов, а значение локальной переменной в <n>
равно objectref
.
Итак, в вашем примере оператор Object foo = null
переводит на следующее:
- Нажмите
null
(специальная ссылка, которая указывает на "ничего" ) на верхнюю часть стека операндов.
operand stack
__________
| null | <-- null is pushed on the operand stack
|__________|
| |
|__________|
| |
|__________|
- Поставьте ссылку из стека операндов и сохраните ее в локальной переменной в индексе 1. Эта локальная переменная соответствует
foo
.
operand stack local variables
__________ _______________ _______________ _______________ _______________
| | | args | foo (null) | | |
|__________| |_______0_______|_______1_______|_______2_______|_______3_______|
| | store null in LV#1
|__________|
| |
|__________|
Те же шаги выполняются для Object bar = null
, за исключением того, что null
хранится в локальной переменной в индексе 2.
Источник: спецификация виртуальной машины Java (см. этот раздел).
Ответ 2
Вы должны посмотреть на структуру структуры стека Java.
Кадр java stack содержит 3 вещи:
- Локальная таблица переменных
- Стек операнда
- Ссылка на постоянный пул классов AKA Frame Datali >
Итак, push a null reference onto the stack
→ подталкивает ссылку на стек операнда.
store a reference into local variable 1
→ сохраняет ссылку в слот 1 таблицы локальных переменных
Ответ 3
Вы можете думать о стеке операнда как временные переменные. Он локально для каждого вызова метода, и его размер можно определить во время компиляции.
Если вы хотите что-либо делать с любыми переменными (локальные переменные, статические переменные или нестатические переменные), вы делаете это через стек операнда. Инструкции Java Bytecode работают в основном только с стеком операндов.
Например,
-
foo = bar
будет соответствовать aload_2
и astore_1
, что просто означает, что значение локальной переменной 2 на стеке операндов и поместить все поверх стека операндов на локальную переменную 1
-
if (foo == null) ...
будет соответствовать aload_1
и ifnonnull 5
, где последний сообщает JVM: если что-то поверх стека операндов не равно null, переходите к следующим 5 смещениям команды; в противном случае переходите к следующей инструкции.
-
int x = args.length
будет соответствовать aload_0
, arraylength
, istore_3
, что означает локальную переменную 0, поместить массив поверх стека операндов и вытолкнуть его длину назад, поместить целое число и сохранить его в локальном переменная 3
- Численные операции, такие как
iadd
, isub
, imul
, idiv
выставляют два целочисленных значения из стека операндов и возвращают результат
- При вызове метода стек операнда вызывается и передается в качестве аргументов локальным переменным нового метода.
-
putstatic
/getstatic
вызывает/ставит/статические переменные
-
putfield
/getfield
всплывает/толкает/нестатические переменные
Ответ 4
Это тот же самый стек.
Или, по крайней мере, вы можете думать о нем как о том, что тот же самый стек, это фактически зависит от реализации jvm.
В простом jvm
При вызове метода он резервирует пространство для локальных переменных в стеке. Он в основном увеличивает указатель стека на открытое пространство для него локальных переменных. Родительский объект метода (если метод экземпляра) и аргументы метода являются первыми локалями.
Чтобы назначить что-то из стека локальному var, нужно скопировать из верхней части стека в соседний адрес, несколько позиций до, в той же области памяти.
Во время astore 1
в вашем примере:
locals/stack
[local 0] // args
[local 1] // foo <--+
[local 2] // bar |
..return address.. |
[stack 0] // null ---+