【问题标题】:Performance issue with Splitting long log line into two将长日志行分成两部分的性能问题
【发布时间】:2017-05-23 06:58:58
【问题描述】:

当我们为了可读性将一长行代码分成两行时,它会在两行之间引入一个加号(如果在文本之间拆分)。 例如,在记录一些文本的文本中间分割一长行。

那么,考虑到它带来的额外字符串连接,应该避免这种情况吗?还是以更好的可读性为代价的权衡更高?

【问题讨论】:

  • 我有 99% 的把握认为编译器会为你把它变成一个不错的大字符串字面量
  • Gene 或 pveentjer 的回答可能应该被标记为正确的回答。

标签: java string concatenation


【解决方案1】:

视情况而定。

如果只有常量,javac 会处理它。以下程序:

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

变成如下字节码(去掉了一些不相关的部分):

public class com/hazelcast/Main {

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 9 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "foobar"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 10 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1
}

您可以在其中看到 foobar 常量。所以在这种情况下,性能不会受到影响。

但是,如果我们将程序更改为更现实的东西:

public class Main {

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

我们得到以下字节码:

public class com/hazelcast/Main {

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 9 L0
    ICONST_1
    ISTORE 1
   L1
    LINENUMBER 10 L1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ILOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
    LDC "foo"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "bar"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L2
    LINENUMBER 11 L2
    RETURN
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
    LOCALVARIABLE a I L1 L3 1
    MAXSTACK = 3
    MAXLOCALS = 2
}

如您所见,“foo”和“bar”不会自动连接。

我不知道 JIT 是否能够删除不需要的串联。

【讨论】:

    【解决方案2】:

    所有常见的 Java 编译器都太成熟了,无法在运行时做一些愚蠢的事情,比如连接字符串文字。让我们检查。鉴于此代码:

    public class CatStrings {
      public static void main(String [] args) {
        String a = "This is a long long long string broken up "
            + "into parts to see if the compiler "
            + "optimizes the concatenation.";
        System.out.println(a);
      }
    }
    

    我的 Java 8 编译器 - Oracle 标准 - 执行正确的操作,如 javap 输出所示:

      stack=2, locals=2, args_size=1
         0: ldc           #2                  // String This is a long long long string broken up into parts to see if the compiler optimizes the concatenation.
         2: astore_1
         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: aload_1
         7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        10: return
    

    【讨论】:

      【解决方案3】:

      是的,对性能有一点影响。常规编译器不做任何优化。它只是将 + 运算符替换为 StringBuilder 方法调用。例如。如果你有String s = "x" + "y" + "z";,在编译成字节码之前,它可能会被String s = new StringBuilder().append("x").append("y").append("z"); 替换。但是这里没有真正的优化。在生成字节码之前,它不会用String s = "xyz"; 替换它。我不确定 JIT 编译器是否在生成本机处理器指令之前对其进行了优化。但即使是这样,它在运行时也会对性能造成很小的影响。

      就个人而言,如果它们以微小的性能损失为代价,我会更关心优雅和可读性。

      【讨论】:

      • 投反对票的人,愿意解释一下你对这个答案的仇恨吗?
      猜你喜欢
      • 2021-09-03
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 2019-04-27
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      相关资源
      最近更新 更多