Как JVM узнает, сколько значений нужно вставить в новый фрейм при вызове метода через invokevirtual?

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

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

2 ответа

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

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

Операнд счетчика инструкции invokeinterface записывает меру количества значений аргумента, где значение аргумента типа long или типа double добавляет две единицы к значению счетчика , а аргумент любого другого типа добавляет одну единицу. Эта информация также может быть получена из дескриптора выбранного метода. Избыточность является исторической.

Эта историческая избыточность не меняет того факта, что JVM должна работать с дескрипторами методов в качестве источника этой информации, например, для invokevirtual, invokestatic, invokespecial, или же invokedynamic. Кроме того, для проверкитребуетсяэтой информации соответствующая JVM , чтобы выдать ошибку, если invokeinterfacecount отличается от счетчика, полученного из дескриптора метода.

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

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

Не существует одного «правильного» способа сделать это, но самая простая стратегия — оставить значения в стеке, а вызываемый метод ссылается на них через отрицательные смещения. Например, если вызываемый метод имеет 3 параметра, на них ссылаются из смещения базового стека минус 3, 2 и 1. Каждый из них копируется в локальную переменную, а затем ссылается обычным образом. Смещение стека можно обновить, чтобы отразить использование параметров. Конечно, каждый локальный параметр также может быть первоначально назначен набором всплывающих окон, по одному для каждого параметра.

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

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

Другие вопросы по тегам