Как отключить оптимизацию конкатенации строк - PullRequest
3 голосов
/ 03 ноября 2019

В Java 9 Oracle улучшена конкатенация строк. Теперь "" + someBoolean превращается в invokedynamic с StringConcatFabric.makeConcat в качестве метода начальной загрузки. Эта структура генерирует классы во время выполнения, которые объединяют ваши строки. Я хочу отключить это поведение и вернуться к простой старой сборке строк.
Так что я предположил, что у javac есть флаг, который делает то, что я хочу. Но я не могу найти это.

1 Ответ

5 голосов
/ 03 ноября 2019

Функция объединения строк состоит из двух частей.

  1. Во время выполнения

    В Java 9+ во время выполнения объединение строк являетсяконтролируется классом StringConcatFactory ( javadoc ). Это потому, что javac генерирует invokedynamic байт-код для StringConcatFactory :: makeConcat` везде, где требуется конкатенация строк.

    StringConcatFactory определяет несколько стратегий для выполнения конкатенации в виде Strategy enum ( исходный код ).

    Вы можете изменить стратегию по умолчанию из командной строки, установив -Djava.lang.invoke.stringConcat

    Чтобы получить поведение Java-8 во время выполнения,вам нужно установить его на BC_SB, что означает "Bytecode, StringBuilder"

    Вот другие значения для полноты:

    /**
     * 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
    
  2. Во время компиляции

    Как правильно заметил Каяман, StringConcatFactory влияет на программу только при времени выполнения . Байт-код будет по-прежнему содержать от invokedynamic до StringConcatFactory везде, где объединяются строки. Существует несколько способов вернуть вызовы на StringBuilder:

    • Самый простой подход к отключению этого поведения - передать флаг --release=8 в javac, чтобы вызвать генерацию Java. -8 совместимый код. Однако это влияет не только на конкатенацию строк.

    • Более целенаправленным вариантом является конкретное управление конкатенацией путем передачи -XDstringConcat=inline.

      Давайте возьмем этот фрагмент кода какпример:

      public class Print {    
          public static void main(String[] args) {
              String foo = "a";
              String bar = "b";
              System.out.println(foo+bar);
          }
      }
      

      Если мы скомпилируем его без каких-либо флагов, мы получим:

      public class Print {
        public Print();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
      
        public static void main(java.lang.String[]);
          Code:
             0: ldc           #2                  // String a
             2: astore_1
             3: ldc           #3                  // String b
             5: astore_2
             6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
             9: aload_1
            10: aload_2
            11: invokedynamic #5,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
            16: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            19: return
      }
      

      Обратите внимание на invokedynamic до makeConcatWithConstants.

      Однако, если мы запустим javac -XDstringConcat=inline Print.java, мы получим это:

      public class Print {
        public Print();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
      
        public static void main(java.lang.String[]);
          Code:
             0: ldc           #2                  // String a
             2: astore_1
             3: ldc           #3                  // String b
             5: astore_2
             6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
             9: new           #5                  // class java/lang/StringBuilder
            12: dup
            13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
            16: aload_1
            17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            20: aload_2
            21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            27: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            30: return
      }
      

      Здесь конкатенация String выполняется с использованием StringBuilder, как в Java 8.

...