Подтвердить что ты не робот

Есть ли лучшее объяснение рамок карты стека?

Недавно я смотрел на Java Virtual Machine Specifications (JVMS), чтобы лучше понять, что заставляет мои программы работать, но я нашел раздел, который я не совсем понимаю...

Раздел 4.7.4 описывает атрибут StackMapTable, и в этом разделе документ подробно описывает кадры карты стека. Проблема в том, что она немного многословна, и я лучше всего научился примеру; не путем чтения.

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

Во всяком случае, у меня есть два конкретных вопроса:

  • Что делают кадры карты стека?
  • Как создается первый кадр карты стека?

и один общий вопрос:

  • Может ли кто-нибудь дать объяснение менее многословным и понятным, чем тот, который задан в JVMS?
4b9b3361

Ответ 1

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

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

Дело в том, что байт-код сам по себе не содержит информации о явном типе. Типы определяются неявно посредством анализа потока данных. Например, инструкция iconst создает целочисленное значение. Если вы храните его в слоте 1, этот слот теперь имеет int. Если поток управления сливается из кода, в котором вместо этого хранится float, в слоте теперь считается недопустимый тип, что означает, что вы не можете ничего сделать с этим значением до перезаписывания.

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

Однако проверка замедляет загрузку классов в Java. Oracle решила решить эту проблему, добавив новый, более быстрый верификатор, который может проверять байт-код за один проход. Для этого им потребовались все новые классы, начиная с Java 7 (с Java 6 в переходном состоянии) для переноса метаданных об их типах, чтобы байт-код можно было проверить за один проход. Поскольку сам формат байт-кода не может быть изменен, информация этого типа хранится отдельно в атрибуте StackMapTable.

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

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

Итак, это основная идея работы StackMapTable и почему она была добавлена. Последний вопрос заключается в том, как создается неявный начальный кадр. Разумеется, ответ заключается в том, что в начале метода стек операндов пуст, а локальные слоты переменных имеют типы, заданные типами параметров метода, которые определяются из метода decriptor.

Если вы привыкли к Java, есть несколько незначительных отличий от того, как типы параметров метода работают на уровне байт-кода. Во-первых, виртуальные методы имеют неявный this в качестве первого параметра. Во-вторых, boolean, byte, char и short не существуют на уровне байт-кода. Вместо этого все они реализованы как ints за кулисами.