【问题标题】:How to turn off string concatenation optimization如何关闭字符串连接优化
【发布时间】:2020-02-29 01:14:19
【问题描述】:

在 Java 9 中,Oracle 改进了字符串连接。现在"" + someBoolean 变成invokedynamicStringConcatFabric.makeConcat 作为引导方法。该结构在运行时生成连接您的字符串的类。我想禁用此行为并回退到普通的旧字符串生成器。
所以我认为 javac 有做我想做的事情的标志。但我找不到它。

【问题讨论】:

  • 为什么要关闭它?
  • 因为我必须从我们的 java fork 中删除所有运行时类生成。这是因为我们的反作弊。我们真的没有其他办法了。
  • 我猜你指的是这个变化:jep280
  • @ЮрийБаринов 所以你还删除了 JDK 内部对 lambda 表达式、方法引用、反射和 Proxy 的所有使用?不允许访问注释?甚至 XML 解析器/XSLT 处理器也使用运行时类生成,谁知道还有什么用它呢。
  • 我在编译时生成代理和 Lambda。所以我们在注释和 lambda 方面没有问题。反射仅在 5 次调用方法后生成类,所以我只是删除了这个功能。但是我为一些硬编码的方法生成访问器。关于 XML。我们只是不使用 XML 解析器。

标签: java jvm concatenation javac string-concatenation


【解决方案1】:

字符串连接功能有两个部分。

  1. 在运行时

    在 Java 9+ 中,在运行时,字符串连接由 StringConcatFactory 类 (javadoc) 控制。这是因为javac 在需要字符串连接的地方生成invokedynamic 字节码到StringConcatFactory::makeConcat

    StringConcatFactoryStrategy 枚举 (source code) 的形式定义了几种 runtime 连接策略。

    您可以通过设置-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. 编译时

    正如 Kayaman 正确指出的那样,StringConcatFactory 仅在 运行时 影响程序。字节码仍将包含invokedynamicStringConcatFactory,只要字符串连接在一起。有几种方法可以回复对 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
      }
      

      注意invokedynamicmakeConcatWithConstants

      但是,如果我们运行 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 中一样。

【讨论】:

    【解决方案2】:

    从 Java 15 开始,StringConcatFactory 没有提供除 MH_INLINE_SIZED_EXACT 之外的其他连接策略。请参阅邮件列表中的this threadthis bug 了解详情。

    正如 Malt 在 his answer 中已经指出的那样,您现在只能通过在编译时禁用 invokedynamic 来返回到 StringBuilders。为此,请将-XDstringConcat=inline 标志传递给编译器。上述更多细节参考了 Malt 的回答。

    如果使用来自 Maven Central 的 JAR 文件,you will need to recompile them yourself

    【讨论】:

      猜你喜欢
      • 2010-09-29
      • 1970-01-01
      • 2011-06-12
      • 2017-06-10
      • 1970-01-01
      • 2017-09-16
      • 1970-01-01
      • 1970-01-01
      • 2021-11-06
      相关资源
      最近更新 更多