java Обозначение байт-кода JVM, грамматика комментариев. InvokeDynamic - PullRequest
4 голосов
/ 28 апреля 2020

Вопрос: Что означает строка 14?

Используйте javap -v - c для разборки следующего кода:

 public class test {
     static int i = 2;
     public static void main(String[] args) {
         test x = new test();
         System.out.println("text + String: " + i);
     } 
 }

в основной функции мы получаем следующее:

14: invokedynamic #20,  0             // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
19: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
...
BootstrapMethods:
  0: #38 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #44 text + String: \u0001

Так, например, строка 19 означает, что invokevirtual функция из элемента # 24 в пуле постоянных времени выполнения. Вызван метод println() из класса java/io/PrintStream, его входные данные из класса Ljava/lang/String, возвращаемое значение - Void.

Что касается строки 14, # 0 содержит ссылку на BootstrapMethod и возвращает объект, чей класс CallSite верно? Тогда:

  1. На что указывает # 20?
  2. Что означает комментарий #0:makeConcatWithConstants:(I)Ljava/lang/String;?

Кроме того, где можно найти дополнительную информацию о грамматике кода разборки Javap? или какое правильное ключевое слово? Документ Oracle о the JVM instruction set, похоже, не дает четкого описания значения комментария.

Ответы [ 2 ]

3 голосов
/ 29 апреля 2020

Короткая версия: Java использует invokedynami c для объединения строк, начиная с Java 9.

Давайте разберем это немного:

Invokedynami c имеет два шага :

  • Когда инструкция вызывается в первый раз, вызывается метод bootstrap. Когда он вернется, сайт вызова будет связан с результатом метода bootstrap.
  • Последующие вызовы будут напрямую вызывать цель MethodHandle .

CallSite является просто держателем для этого MethodHandle . В зависимости от используемого подкласса CallSite сайт может быть позднее перекомпонован.

Если мы посмотрим на инструкцию, в конце мы увидим следующее:

#0:makeConcatWithConstants:(I)Ljava/lang/String;

Первая часть (#0) означает: Bootstrap метод # 0.
Вторая часть - это имя, которое передается методу bootstrap и может или не может использоваться там.
Третья часть - тип метода итоговой цели. В нашем случае: метод, который принимает int и возвращает java.lang.String.

Если мы теперь посмотрим на bootstrap метод # 0, мы увидим ссылку на метод, здесь на StringConcatFactory.makeConcatWithConstants (...) . Мы также видим, что есть дополнительный аргумент: String "text + String: \u0001".

Задача метода bootstrap теперь состоит в том, чтобы вернуть MethodHandle (внутри CallSite), который в этом случае выполняет конкатенацию строк. Но как это происходит для конкатенации строк (StringBuilder, String.format, вращение байт-кода, цепочка MethodHandles ...) не имеет значения для фактического класса. Нужно только объединить строки.


Давайте попробуем эмулировать это поведение вручную. В конце концов, метод bootstrap - это обычный метод Java:

public static void main(String[] args) throws Throwable {
    CallSite cs = StringConcatFactory.makeConcatWithConstants(MethodHandles.lookup(),
            "makeConcatWithConstants", MethodType.methodType(String.class, int.class),
            "text + String: \u0001");

    int x = 2;
    String result = (String) cs.dynamicInvoker().invokeExact(x);
    System.out.println(result);

    x = 3;
    result = (String) cs.dynamicInvoker().invokeExact(x);
    System.out.println(result);
}

(ВМ делает еще кое-что, например, запоминает результат и больше не будет вызывать метод bootstrap, но для нашего небольшого примера это достаточно хорошо).


На данный момент мы можем заглянуть под капот о том, как метод bootstrap выполняет свою работу.
Получается: вы можете настройте виртуальную машину для использования различных стратегий.
И она использует свое привилегированное положение внутри java.base для доступа к частному конструктору пакета для java .lang.String, который не копирует массив - что безопасно, если содержимое впоследствии не изменяется.

Стратегия по умолчанию - MethodHandle Chaining.

Хорошая новость: если кто-то напишет в какой-то момент лучшую стратегию, ваша программа выиграет от нее - без перекомпиляции.

2 голосов
/ 29 апреля 2020

См. спецификацию JVM :

Во-первых, беззнаковые indexbyte1 и indexbyte2 используются для создания индекса в пуле постоянных времени выполнения текущего класса (§ 2.6), ... Запись пула констант времени выполнения в индексе должна быть символической c ссылкой на сайт динамически вычисляемых вызовов (§5.1).

Удобно, что javap уже ищет постоянный пул и декодирует информацию; В результате получается то, что было напечатано в виде комментария за инструкцией в строке

14: invokedynamic #20,  0             // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;

Число #0 - это индекс атрибута BootstrapMethods, который вы уже опубликовали. Значение имени метода соответствует этому bootstrap методу. Кроме того, имеется дескриптор типа (I)Ljava/lang/String;, поэтому этот указанный вызов c потребляет int и создает String.

Что произойдет во время выполнения, зависит от того, на который есть ссылка bootstrap метод. Этот вызов относится к методу static StringConcatFactory.makeConcatWithConstants(...), частично, конкатенация строк скомпилирована с Java 9.

Документация этого метода сообщает Мы считаем, что имя метода, используемое в инструкции invokedynamic, не имеет значения, а аргумент stati c атрибута BootstrapMethod, то есть text + String: \u0001, определяет формат строки. \u0001 является заполнителем для «обычного аргумента», то есть параметра int.

...