Символические ссылки в Java
В эти дни я играл с отражением Java и .class
формат. Я сейчас учусь ldc
инструкция.
В Спецификации JVM я нашел термин, который я не понимаю: символьная ссылка, и у меня есть следующие вопросы.
Что это значит?
Где это используется?
- В каких случаях
ldc
инструкция загрузить символическую ссылку? - Есть ли в Java код, соответствующий этому действию?
2 ответа
Было бы полезно, если бы вы указали точную часть документации, которая доставляет вам неприятности. Поскольку вы этого не сделали, я собираюсь высказать предположение о том, что вы могли бы процитировать из документа для ldc:
В противном случае, если запись пула констант во время выполнения является символической ссылкой на класс (§5.1), то именованный класс разрешается (§5.4.3.1), и ссылка на объект Class, представляющий этот класс, значение, помещается на стек операндов.
В противном случае запись пула констант во время выполнения должна быть символической ссылкой на тип метода или дескриптор метода (§5.1). ...
Эта цитата содержит ссылку на другой раздел спецификации JVM (5.1), который описывает пул констант во время выполнения:
структура данных времени выполнения, которая служит многим целям таблицы символов обычной реализации языка программирования
Это означает, что пул констант во время выполнения содержит информацию о фрагментах класса в символической форме: в виде текстовых значений.
Так когда ldc
дается "символическая ссылка" на класс, ему дается индекс CONSTANT_Class_info
структура в постоянном пуле. Если вы посмотрите на определение этой структуры, то увидите, что она содержит ссылку на имя класса, также содержащегося в пуле констант.
TL; DR: "символьные ссылки" - это строки, которые можно использовать для извлечения фактического объекта.
Пример:
if (obj.getClass() == String.class) {
// do something
}
Получается следующий байт-код:
aload_1
invokevirtual #21; //Method java/lang/Object.getClass:()Ljava/lang/Class;
ldc #25; //class java/lang/String
if_acmpne 20
В этом случае ldc
Операция относится к классу, который хранится символически. Когда JVM выполняет этот код операции, он будет использовать символическую ссылку для идентификации фактического класса в текущем загрузчике классов и возвращать ссылку на экземпляр класса.
Чтобы добавить к другому ответу только части 1 и 2 вопроса:
В настоящее время JVM использует области данных времени выполнения, которые можно разделить на шесть областей:• Регистр счетчика программ (ПК) • Стеки виртуальной машины Java (JVM) • Стеки собственных методов • Область кучи • Область методов • Пул констант времени выполнения
Регистр ПК используется для хранения адреса следующей инструкции, которая представляет собой код инструкции, подлежащей выполнению. Механизм выполнения считывает следующую инструкцию, и JVM использует ее для отслеживания выполнения потоков, поскольку ЦП будет постоянно переключаться между ними.
Фрейм стека состоит из трех частей: массива локальных переменных, стека операндов и данных фрейма.
- Стек операндов предназначен для любых промежуточных операций, которые могут потребоваться, таких как сложение или вычитание чисел. Стек операндов действует как рабочее пространство времени выполнения для выполнения операции.
- Массив локальных переменных содержит все параметры и локальные переменные метода.
- Данные кадра: содержат все символические ссылки (постоянное разрешение пула) и обычные возвраты метода, связанные с этим конкретным методом.
Собственные стеки используются, когда реализация виртуальной машины Java использует обычные стеки, в просторечии называемые «стеками C», для поддержки собственных методов (методов, написанных на языке, отличном от языка программирования Java).
Куча — это область данных времени выполнения, из которой выделяется память для всех экземпляров классов и массивов.
Виртуальная машина Java имеет область методов, совместно используемую всеми потоками виртуальной машины Java. Область метода аналогична области хранения скомпилированного кода обычного языка или аналогична «текстовому» сегменту в процессе операционной системы. Он хранит структуры для каждого класса, такие как пул констант времени выполнения, данные полей и методов, а также код для методов и конструкторов, включая специальные методы, используемые при инициализации класса и интерфейса и при инициализации экземпляра (§2.9).
Пул констант времени выполнения — это представление времени выполнения для каждого класса или интерфейса таблицы Constant_pool в файле класса (§4.4).
Так что же это означает в практическом плане? Насколько я понимаю, это абстракция для ссылок на классы без каких-либо побочных эффектов.
Например, что, если вы использовали объект класса вместо символов константного пула для хранения структур классов во время выполнения, и он не загружался должным образом? Я видел базу кода с несколькими jar-файлами с одинаковым именем, типом и версией, загруженными из разных областей кода, потому что несколько команд работали над отдельными разделами большой базы кода и не удосужились навести порядок. . Я также работал с кортежами строк как средством построения уникального местоположения в транспортной программе. Это работало, но казалось, что это был неуклюжий механизм для обработки варианта использования. Итак, я хочу сказать, что символические представления для классов и интерфейсов удовлетворяют эту потребность и создают стандартную процедуру для ссылки на классы во время выполнения без побочных эффектов, насколько мне известно.
Процитируем Брайана Гетца по этому поводу:
Такие действия, как генерация байт-кода, часто требуют описания констант, таких как классы. Однако объект Class — плохое описание произвольного класса. Создание экземпляра класса имеет множество зависимостей от среды и режимов отказа; загрузка может завершиться ошибкой, поскольку требуемый класс не существует или может быть недоступен запрашивающей стороне, результат загрузки зависит от контекста загрузки класса, загрузка классов имеет побочные эффекты, а иногда может быть вообще невозможно (например, когда описываемые классы еще не существуют или не могут быть загружены по какой-либо иной причине, например, во время компиляции тех же классов или во время преобразования jlink.) Таким образом, хотя класс String является прекрасным описанием для Constant_String_info, тип Class не является очень хорошее описание для Constant_Class_info.
Ряд действий разделяет необходимость иметь дело с классами, методами и другими сущностями в чисто номинальной форме. Библиотеки разбора и генерации байт-кода должны описывать классы и дескрипторы методов в символьной форме. Без официального механизма они должны прибегать к специальным механизмам, будь то типы дескрипторов, такие как дескриптор ASM, или кортежи строк (владелец метода, имя метода, дескриптор метода), или специальное (и подверженное ошибкам) кодирование их в одна строка. Начальные загрузчики для invokedynamic, работающие за счет вращения байт-кода (например, LambdaMetafactory), предпочли бы работать в символическом домене, а не с живыми классами и дескрипторами методов. Компиляторы и автономные преобразователи (такие как подключаемые модули jlink) должны описывать классы и элементы для классов, которые не могут быть загружены в работающую виртуальную машину. Плагины компилятора (например, процессоры аннотаций) также должны описывать программные элементы в символьных терминах. Им всем было бы полезно иметь единый официальный способ описания таких констант.
Процитированные и уточненные документы по Java относятся к Java SE 12.
Для иллюстрации этого есть диаграммы: Архитектура JVM от Dzone.
Наконец, это «Введение в пул констант в JVM» от Baeldung, который иллюстрирует простую программу Hello World с байт-кодом.