对String的优化是如何实现的?
yan465942872
2012-03-08
public class Test { public String getS1() { return "a" + "b" + "c" + "d" + "e"; } public String getS2() { return (new StringBuilder("a").append("b").append("c").append("d") .append("e")).toString(); } public String getS3(String[] fields) { String result = ""; for(String field : fields) { result += field; } return result; } public String getS4(String[] fields) { StringBuilder result = new StringBuilder(); for(String field : fields) { result.append(field); } return result.toString(); } } 如上代码,一直认为一般频繁的操作字符串应该用StringBuilder或者StringBuffer。直接看生成的JVM机字节码如下:
public java.lang.String getS1(); Code: Stack=1, Locals=1, Args_size=1 0: ldc #16; //String abcde 2: areturn --------------------------- public java.lang.String getS2(); Code: Stack=3, Locals=1, Args_size=1 0: new #19; //class java/lang/StringBuilder 3: dup 4: ldc #21; //String a 6: invokespecial #23; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 9: ldc #26; //String b 11: invokevirtual #28; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #32; //String c 16: invokevirtual #28; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: ldc #34; //String d 21: invokevirtual #28; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: ldc #36; //String e 26: invokevirtual #28; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: invokevirtual #38; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: areturn ---------------------------------- public java.lang.String getS3(java.lang.String[]); Code: Stack=3, Locals=7, Args_size=2 0: ldc #43; //String 2: astore_2 3: aload_1 4: dup 5: astore 6 7: arraylength 8: istore 5 10: iconst_0 11: istore 4 13: goto 44 16: aload 6 18: iload 4 20: aaload 21: astore_3 22: new #19; //class java/lang/StringBuilder 25: dup 26: aload_2 27: invokestatic #45; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 30: invokespecial #23; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 33: aload_3 34: invokevirtual #28; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 37: invokevirtual #38; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 40: astore_2 41: iinc 4, 1 44: iload 4 46: iload 5 48: if_icmplt 16 51: aload_2 52: areturn -------------------------------------- public java.lang.String getS4(java.lang.String[]); Code: Stack=2, Locals=7, Args_size=2 0: new #19; //class java/lang/StringBuilder 3: dup 4: invokespecial #59; //Method java/lang/StringBuilder."<init>":()V 7: astore_2 8: aload_1 9: dup 10: astore 6 12: arraylength 13: istore 5 15: iconst_0 16: istore 4 18: goto 36 21: aload 6 23: iload 4 25: aaload 26: astore_3 27: aload_2 28: aload_3 29: invokevirtual #28; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: pop 33: iinc 4, 1 36: iload 4 38: iload 5 40: if_icmplt 21 43: aload_2 44: invokevirtual #38; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 47: areturn
能看出来getS1()比getS2()效率高,但是getS4()比getS3()效率高.也就是说"a" + "b" + "c" + "d" + "e" 在编译时直接优化成了"abcde",运行时确认的是优化成了StringBuilder了。
我认为是如果是不是运行时决定,在编译优化后效率String的"+"运算反而比StringBuilder或者StringBuffer的append效率高,如果是运行时确定,则后两者效率相对高一点,这样说对吗?对String的优化是这样实现的吗? 注:java version "1.6.0_31"
|
|
RednaxelaFX
2012-03-08
yan465942872 写道 我认为是如果是不是运行时决定,在编译优化后效率String的"+"运算反而比StringBuilder或者StringBuffer的append效率高,如果是运行时确定,则后两者效率相对高一点,这样说对吗?对String的优化是这样实现的吗? 这段话恕我没看懂。请问能重新组织一下语言,把条理整理一下再说一次么? ========================== 这些都不是JVM问题,而是Java语言/标准库相关的问题。不建议在这个群组讨论。 其实就几个点: 1、常量表达式的拼接用"+"好。 "a" + "b" 是常量表达式,而 new StringBuilder("a").append("b").toString() 不是常量表达式。 如果对Java的常量表达式没有准确的理解,请阅读Java语言规范(不是JVM规范):http://rednaxelafx.iteye.com/blog/1081626 2、对非常量表达式,JDK6的javac的实现是每个语句内连续的字符串拼接(“+”)会对应一个新的StringBuilder实例。循环里+=就是每轮循环都新建StringBuilder。这很直观。当然,能少创建StringBuilder,并且减少扩容的做法会比较高效。 |
|
yan465942872
2012-03-08
RednaxelaFX 写道 yan465942872 写道 我认为是如果是不是运行时决定,在编译优化后效率String的"+"运算反而比StringBuilder或者StringBuffer的append效率高,如果是运行时确定,则后两者效率相对高一点,这样说对吗?对String的优化是这样实现的吗?
这段话恕我没看懂。请问能重新组织一下语言,把条理整理一下再说一次么? 我的错。我原本认为:javac对"a" + "b"进行了优化。 ------------------------------- 现在想来,是对所有的常量表达式都是这样处理的。看了点jvm就开始把学了没多久的编译原理给忘了。看来我还是太年轻,基础知识不扎实 。上面讲的很详细,感谢RednaxelaFX的耐心的回答。 |