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

Когда была введена оптимизация Javac StringBuilder/StringBuffer?

Я знаю, что Javac-компилятор способен преобразовать конкатенацию строк + с помощью StringBuilder/StringBuffer, и мне любопытно узнать , начиная с которого версия была введена это изменение?

Я использую этот пример кода:

public class Main {
  public static void main(String[] args) {
      String a = args[0];
      String s = "a";
      s = s + a;
      s = s + "b";
      s = s + "c";
      s = s + "d";
      s = s + "e";
      System.out.println(s);
  }
}

До сих пор я пробовал использовать javac 1.8.0_121, javac 1.6.0_20, javac 1.5.0_22 и java 1.4.2_19.

Вот пример байт-кода, который я вижу с помощью javap -c из 1.4.2_19:

6:  astore_2
7:  new #3; //class StringBuffer
10: dup
11: invokespecial   #4; //Method java/lang/StringBuffer."<init>":()V
14: aload_2
15: invokevirtual   #5; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
18: aload_1
19: invokevirtual   #5; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
22: invokevirtual   #6; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;

Кажется, что все 4 версии используют оптимизацию StringBuilder/StringBuffer, поэтому мне интересно узнать , начиная с которой была изменена версия Javac?

4b9b3361

Ответ 1

Здесь приведена цитата из спецификации языка из версии 1:

Реализация может выбрать выполнение преобразования и конкатенации за один шаг, чтобы избежать создания и затем отбрасывания промежуточного объекта String. Чтобы увеличить производительность повторной конкатенации строк, компилятор Java может использовать класс StringBuffer (§20.13) или подобный метод для уменьшения числа промежуточных объектов String, созданных при оценке выражения.

В то время они были StringBuffer вместо StringBuilder.

Также цитата из StringBuffer JDK1.0.2:

Этот класс является растущим буфером для символов. Он в основном используется для создания строк. Компилятор использует его для реализации оператора "+".

Ответ 2

Я изучил спецификацию языка Java, первое издание (с 1996 года). Нелегкая находка, но здесь. Проход по оптимизации конкатенации был там даже тогда:

Реализация может решить выполнить преобразование и конкатенацию за один шаг, чтобы избежать создания и затем отбрасывания промежуточного объекта String. Чтобы повысить производительность повторной конкатенации строк, компилятор Java может использовать класс StringBuffer (§20.13) или подобный метод для уменьшения количества промежуточных объектов String, созданных при оценке выражения.

Спецификация относится к StringBuffer, тогда, но StringBuilder (к которой относится текущая формулировка JLS) может считаться более эффективной, поскольку ее методы не синхронизируются.

Это, однако, не означает, что нужно полагаться на оптимизацию, как всегда на месте. Например, конкатенация строк в цикле не будет оптимизирована.

Ответ 4

Это не отвечает на вопрос, но я хочу просто добавить к общей точке, что в jdk-9 эта StringBuilder::append является одной из разрешенных стратегий, но не стандартной.

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
}

На самом деле это байт-код invokedynamic для конкатенации строк, поэтому его реализация теперь специфична для JRE, а не для компилятора. Стратегия по умолчанию btw: MH_INLINE_SIZED_EXACT