中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > Java > java高级编程
String连接操作比StringBuffer产生更多的对象吗?
作者:未知 时间:2005-07-24 21:15 出处:JR 责编:chinaitpower
              摘要:String连接操作比StringBuffer产生更多的对象吗?
前天写了一篇文章Java性能陷阱:StringBuffer的性能真的比String好吗?,有两位网友进行了评论,而且看到得到了三张反对票,因此感觉有必要进行更深入的说明和研究。针对网友评论中提到的字符串直接连接操作会产生更多的垃圾对象的问题并联想到那篇文章的缘起文章(使用String还是StringBuffer?(cherami))中提到的反编译类文件的技巧,感觉可以实际的验证一下,因此又做了如下的测试。

第一步,准备工作


为了方便后面测试工作的进行,有必要编写一个简单的脚本:
echo test by jdk1.2.2
/opt/java/jdk1.2.2/bin/javac StringCompileTest.java
/opt/java/jdk1.2.2/bin/javap -c StringCompileTest
echo test by jdk1.3.1_09
/opt/java/jdk1.3.1_09/bin/javac StringCompileTest.java
/opt/java/jdk1.3.1_09/bin/javap -c StringCompileTest
echo test by jdk1.4.2
/opt/java/jdk1.4.2/bin/javac StringCompileTest.java
/opt/java/jdk1.4.2/bin/javap -c StringCompileTest

上面的脚本根据需要可以应用在windows或者linux上,我是在linux进行测试的,因此我把它保存为一个文件stringdecompile.sh,如果你在windows上测试,你可以保存为stringdecompile.bat。

第二步,测试代码


  1. public class StringCompileTest {
  2.     String s1="This is a ";
  3.     String s2="string decompile ";
  4.     String s3="testing.";
  5.     public void stringTest() {
  6.       String s=s1+s2+s3;
  7.     }
  8.     public void stringBufferTest() {
  9.       StringBuffer buffer=new StringBuffer();
  10.       buffer.append(s1);
  11.       buffer.append(s2);
  12.       buffer.append(s3);
  13.       String s=buffer.toString();
  14.     }
  15. }

运行结果:
test by jdk1.2.2
Compiled from StringCompileTest.java
public class StringCompileTest extends java.lang.Object {
    java.lang.String s1;
    java.lang.String s2;
    java.lang.String s3;
    public StringCompileTest();
    public void stringBufferTest();
    public void stringTest();
}
 
Method StringCompileTest()
   0 aload_0
   1 invokespecial #8 <Method java.lang.Object()>
   4 aload_0
   5 ldc #1 <String "This is a ">
   7 putfield #12 <Field java.lang.String s1>
  10 aload_0
  11 ldc #2 <String "string decompile ">
  13 putfield #13 <Field java.lang.String s2>
  16 aload_0
  17 ldc #3 <String "testing.">
  19 putfield #14 <Field java.lang.String s3>
  22 return
 
Method void stringBufferTest()
   0 new #7 <Class java.lang.StringBuffer>
   3 dup
   4 invokespecial #9 <Method java.lang.StringBuffer()>
   7 astore_1
   8 aload_1
   9 aload_0
  10 getfield #12 <Field java.lang.String s1>
  13 invokevirtual #11 <Method java.lang.StringBuffer append(java.lang.String)>
  16 pop
  17 aload_1
  18 aload_0
  19 getfield #13 <Field java.lang.String s2>
  22 invokevirtual #11 <Method java.lang.StringBuffer append(java.lang.String)>
  25 pop
  26 aload_1
  27 aload_0
  28 getfield #14 <Field java.lang.String s3>
  31 invokevirtual #11 <Method java.lang.StringBuffer append(java.lang.String)>
  34 pop
  35 aload_1
  36 invokevirtual #15 <Method java.lang.String toString()>
  39 astore_2
  40 return
 
Method void stringTest()
   0 new #7 <Class java.lang.StringBuffer>
   3 dup
   4 aload_0
   5 getfield #12 <Field java.lang.String s1>
   8 invokestatic #16 <Method java.lang.String valueOf(java.lang.Object)>
  11 invokespecial #10 <Method java.lang.StringBuffer(java.lang.String)>
  14 aload_0
  15 getfield #13 <Field java.lang.String s2>
  18 invokevirtual #11 <Method java.lang.StringBuffer append(java.lang.String)>
  21 aload_0
  22 getfield #14 <Field java.lang.String s3>
  25 invokevirtual #11 <Method java.lang.StringBuffer append(java.lang.String)>
  28 invokevirtual #15 <Method java.lang.String toString()>
  31 astore_1
  32 return
test by jdk1.3.1_09
Compiled from StringCompileTest.java
public class StringCompileTest extends java.lang.Object {
    java.lang.String s1;
    java.lang.String s2;
    java.lang.String s3;
    public StringCompileTest();
    public void stringTest();
    public void stringBufferTest();
}
 
Method StringCompileTest()
   0 aload_0
   1 invokespecial #1 <Method java.lang.Object()>
   4 aload_0
   5 ldc #2 <String "This is a ">
   7 putfield #3 <Field java.lang.String s1>
  10 aload_0
  11 ldc #4 <String "string decompile ">
  13 putfield #5 <Field java.lang.String s2>
  16 aload_0
  17 ldc #6 <String "testing.">
  19 putfield #7 <Field java.lang.String s3>
  22 return
 
Method void stringTest()
   0 new #8 <Class java.lang.StringBuffer>
   3 dup
   4 invokespecial #9 <Method java.lang.StringBuffer()>
   7 aload_0
   8 getfield #3 <Field java.lang.String s1>
  11 invokevirtual #10 <Method java.lang.StringBuffer append(java.lang.String)>
  14 aload_0
  15 getfield #5 <Field java.lang.String s2>
  18 invokevirtual #10 <Method java.lang.StringBuffer append(java.lang.String)>
  21 aload_0
  22 getfield #7 <Field java.lang.String s3>
  25 invokevirtual #10 <Method java.lang.StringBuffer append(java.lang.String)>
  28 invokevirtual #11 <Method java.lang.String toString()>
  31 astore_1
  32 return
 
Method void stringBufferTest()
   0 new #8 <Class java.lang.StringBuffer>
   3 dup
   4 invokespecial #9 <Method java.lang.StringBuffer()>
   7 astore_1
   8 aload_1
   9 aload_0
  10 getfield #3 <Field java.lang.String s1>
  13 invokevirtual #10 <Method java.lang.StringBuffer append(java.lang.String)>
  16 pop
  17 aload_1
  18 aload_0
  19 getfield #5 <Field java.lang.String s2>
  22 invokevirtual #10 <Method java.lang.StringBuffer append(java.lang.String)>
  25 pop
  26 aload_1
  27 aload_0
  28 getfield #7 <Field java.lang.String s3>
  31 invokevirtual #10 <Method java.lang.StringBuffer append(java.lang.String)>
  34 pop
  35 aload_1
  36 invokevirtual #11 <Method java.lang.String toString()>
  39 astore_2
  40 return
test by jdk1.4.2
Compiled from "StringCompileTest.java"
public class StringCompileTest extends java.lang.Object{
java.lang.String s1;
 
java.lang.String s2;
 
java.lang.String s3;
 
public StringCompileTest();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   ldc     #2; //String This is a
   7:   putfield        #3; //Field s1:Ljava/lang/String;
   10:  aload_0
   11:  ldc     #4; //String string decompile
   13:  putfield        #5; //Field s2:Ljava/lang/String;
   16:  aload_0
   17:  ldc     #6; //String testing.
   19:  putfield        #7; //Field s3:Ljava/lang/String;
   22:  return
 
public void stringTest();
  Code:
   0:   new     #8; //class StringBuffer
   3:   dup
   4:   invokespecial   #9; //Method java/lang/StringBuffer."<init>":()V
   7:   aload_0
   8:   getfield        #3; //Field s1:Ljava/lang/String;
   11:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   14:  aload_0
   15:  getfield        #5; //Field s2:Ljava/lang/String;
   18:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   21:  aload_0
   22:  getfield        #7; //Field s3:Ljava/lang/String;
   25:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   28:  invokevirtual   #11; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
   31:  astore_1
   32:  return
 
public void stringBufferTest();
  Code:
   0:   new     #8; //class StringBuffer
   3:   dup
   4:   invokespecial   #9; //Method java/lang/StringBuffer."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   aload_0
   10:  getfield        #3; //Field s1:Ljava/lang/String;
   13:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   16:  pop
   17:  aload_1
   18:  aload_0
   19:  getfield        #5; //Field s2:Ljava/lang/String;
   22:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   25:  pop
   26:  aload_1
   27:  aload_0
   28:  getfield        #7; //Field s3:Ljava/lang/String;
   31:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   34:  pop
   35:  aload_1
   36:  invokevirtual   #11; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
   39:  astore_2
   40:  return
 
}

分析


我想我们没有必要仔细的进行分析了,稍稍对比一下,我们就可以看到对于JDK1.3.1和JDK1.4.2而言,编译器在编译代码的时候已经进行了优化,而且实际上字符串直接操作产生的最终代码(操作步骤)其实比用StringBuffer的更短,这就解释了为什么对于JDK1.3.1和JDK1.4.2而言,在次数比较少的情况下,字符串直接操作比StringBuffer的性能实际上更好。
但是问题又来了,那么为什么在次数很多的情况或者进行累加的时候,StringBuffer的性能要好或者好很多呢。我想问题的核心在于StringBuffer多出的那几条指令上,初步看了一下JVM规范对于JVM指令助记符的说明得知那几条多出的只能是对于堆栈的操作,这个可以大致的说明为什么StringBuffer的性能好一些,因为它是进行堆栈的存取,速度可能比内存堆的速度快。至于问题的最终的完整解释需要等我这周看完相关的助记符的意义以后才能有个结果。

总结


从我们实验的结果看,我们最起码可以得到如下的结论:
对于一条字符串直接连接操作而言,它并没有产生中间多余的对象,同样编译器在进行处理的时候将之转换为StringBuffer进行处理了。

但是我感觉可能是我在那篇文章里面对于测试的目的没有说得很清楚,而且最终的结论里面也说得不够完整,
对于累加的情况,确实会产生更多的对象,但是我为了将一条语句执行的时间放大才放在一个循环里面,在实际应用中我想没有谁会那么用。cherami的文章最后得到的结论--如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer--依然是成立的,我担心的是那篇文章对String和StringBuffer使用的另外一方面的误导:由于担心性能问题对单条语句也使用StringBuffer进行替换。

从这次的测试我们还可以更深切的感受到java编译器和JVM或者JRE在不断进行着提高,相信以后性能方面的担忧会更少。由于JDK最初的一些版本,甚至是到了JDK1.2.2还没有解决一些问题而导致网上有很多讨论java性能的问题,但是这些问题对于以后的JDK版本而言可能已经不是问题了。
关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有