对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的耐心的回答。
Global site tag (gtag.js) - Google Analytics