Как осуществляется конкатенация строк в Java 9?


как написано в JEP 280:

изменить статический!--0-->-последовательность байт-кода конкатенации, генерируемая javac использовать invokedynamic вызовы функций библиотеки JDK. Это позволит в будущем оптимизировать String конкатенация без необходимости внесения дополнительных изменений в байт-код, выдаваемый javac.

здесь я хочу понять, в чем польза invokedynamic вызовы и как конкатенация байт-кода отличается от invokedynamic?

3 102

3 ответа:

"старый" способ вывода куча StringBuilder-ориентированные операции. Рассмотрим эту программу:

public class Example {
    public static void main(String[] args)
    {
        String result = args[0] + "-" + args[1] + "-" + args[2];
        System.out.println(result);
    }
}

если мы скомпилируем это с JDK 8 или ранее, а затем используем javap -c Example посмотреть байт-код, мы видим что-то вроде этого:

public class Example {
  public Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: aload_0
       8: iconst_0
       9: aaload
      10: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      13: ldc           #5                  // String -
      15: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: aload_0
      19: iconst_1
      20: aaload
      21: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: ldc           #5                  // String -
      26: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      29: aload_0
      30: iconst_2
      31: aaload
      32: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      35: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      38: astore_1
      39: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      42: aload_1
      43: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      46: return
}

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

давайте посмотрим, что генерирует Java 9:

public class Example {
  public Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: aload_0
       1: iconst_0
       2: aaload
       3: aload_0
       4: iconst_1
       5: aaload
       6: aload_0
       7: iconst_2
       8: aaload
       9: invokedynamic #2,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
      14: astore_1
      15: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      18: aload_1
      19: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      22: return
}

О боже, Но это короче. :- ) Он делает один вызов makeConcatWithConstants С StringConcatFactory, который говорит это в своем Javadoc:

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

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

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


Я бы, вероятно, попытался провести вас через них с изменениями, которые были внесены для реализации оптимизации конкатенации строк.

  • определение метода начальной загрузки:- С Java9, методы начальной загрузки для invokedynamic места вызова, для поддержки конкатенация строк в первую очередь makeConcat и makeConcatWithConstants были введены с StringConcatFactory реализация.

    использование invokedynamic предоставляет альтернативу для выбора стратегии перевода до времени выполнения. Стратегии перевода в StringConcatFactory похож на LambdaMetafactory как представлено в предыдущей версии java. Кроме того, одна из целей JEP упомянутый в вопросе заключается в том, чтобы растянуть эти стратегии дальше.

  • Указание Постоянных Записей Пула:- это дополнительные статические аргументы к invokedynamic обучения (1) MethodHandles.Lookup объект, который является фабрикой для создания дескрипторов метода в контексте invokedynamic инструкция, (2) a String объект, имя метода, упомянутое в сайте динамического вызова и (3)MethodType

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

и второй момент заключается в том, что на данный момент есть 6 possible strategies for concatenation of String:

 private enum Strategy {
    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder}.
     */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
    BC_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also tries to estimate the required storage.
     */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
    MH_INLINE_SIZED_EXACT
}

вы можете выберите любой из них с помощью параметра:-Djava.lang.invoke.stringConcat. Обратите внимание, что StringBuilder - это еще вариант.