`
RednaxelaFX
  • 浏览: 3048937 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

答复: JAVA6可以使用字符串累加

    博客分类:
  • Java
阅读更多
把先前在论坛回复的一些帖打捞进来。这篇的原帖是:JAVA6可以使用字符串累加
下面是回复内容,带补充。

=================================================================

javabkb 写道
听过sajia老师讲的视频 记得1.4后的hotspot就有这个编译优化效果了

<< 我肯定没有这样说过…这帖里说的东西都跟HotSpot(或者其它JVM实现)一点关系都没有,还在Java源码级编译器层次上…

javabkb 写道
但是一直有个问题,即使是编译优化了,用加号拼接的效率还是不如用stringbuilder的

<< 这个也不对…要分情况说。前面也有说了,String的+本来就是用StringBuilder(JDK 5或以上)或StringBuffer(JDK 1.4或以下)实现的。
关键问题是:是不是同一个StringBuilder/StringBuffer。在循环里用+=之类的方式来拼接字符串的问题就出在每轮循环里都new了一个StringBuilder/StringBuffer来做拼接,然后toString()完就抛弃了,等下轮循环进来又再new一个。

sswh 写道
JDK6并没有特别优化。

嗯,从Java字节码一层能看到的情况看,确实是如此的。从StringBuffer转换到StringBuilder也是JDK5的时候做的事情了,而不是JDK6才开始的。

让我演示一下某代码在JDK 1.0.2上编译出来的样子:
public class X {
  public static void main(String[] args) {
    String a = "alpha";
    String b = "beta";
    String c = "charlie";
    String d = a + b + c;
  }
}


Compiled from "X.java"
public class X extends java.lang.Object
  SourceFile: "X.java"
  minor version: 3
  major version: 45
  Constant pool:
const #1 = String       #16;    //  alpha
const #2 = String       #8;     //  beta
const #3 = String       #22;    //  charlie
const #4 = class        #17;    //  java/lang/Object
const #5 = class        #10;    //  X
const #6 = Method       #4.#7;  //  java/lang/Object."<init>":()V
const #7 = NameAndType  #20:#23;//  "<init>":()V
const #8 = Asciz        beta;
const #9 = Asciz        ConstantValue;
const #10 = Asciz       X;
const #11 = Asciz       Exceptions;
const #12 = Asciz       LineNumberTable;
const #13 = Asciz       SourceFile;
const #14 = Asciz       LocalVariables;
const #15 = Asciz       Code;
const #16 = Asciz       alpha;
const #17 = Asciz       java/lang/Object;
const #18 = Asciz       main;
const #19 = Asciz       ([Ljava/lang/String;)V;
const #20 = Asciz       <init>;
const #21 = Asciz       X.java;
const #22 = Asciz       charlie;
const #23 = Asciz       ()V;

{
public static void main(java.lang.String[]);
  Code:
   Stack=1, Locals=4, Args_size=1
   0:   ldc     #1; //String alpha
   2:   astore_1
   3:   ldc     #2; //String beta
   5:   astore_2
   6:   ldc     #3; //String charlie
   8:   astore_3
   9:   return
  LineNumberTable:
   line 3: 0
   line 4: 3
   line 5: 6
   line 2: 9


public X();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #6; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 1: 0


}

<< 留意一下Class文件的版本号,这个是用JDK 1.0.2里的javac编译的,如假包换。
看到了么,main()方法里的字节码根本就没有字符串拼接的动作发生,对吧?

然后用JDK 1.0.2的javac -O来编译:
Compiled from "X.java"
public class X extends java.lang.Object
  SourceFile: "X.java"
  minor version: 3
  major version: 45
  Constant pool:
const #1 = class        #11;    //  java/lang/Object
const #2 = class        #6;     //  X
const #3 = Method       #1.#4;  //  java/lang/Object."<init>":()V
const #4 = NameAndType  #14:#16;//  "<init>":()V
const #5 = Asciz        ConstantValue;
const #6 = Asciz        X;
const #7 = Asciz        Exceptions;
const #8 = Asciz        SourceFile;
const #9 = Asciz        LocalVariables;
const #10 = Asciz       Code;
const #11 = Asciz       java/lang/Object;
const #12 = Asciz       main;
const #13 = Asciz       ([Ljava/lang/String;)V;
const #14 = Asciz       <init>;
const #15 = Asciz       X.java;
const #16 = Asciz       ()V;

{
public static void main(java.lang.String[]);
  Code:
   Stack=0, Locals=1, Args_size=1
   0:   return

public X();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #3; //Method java/lang/Object."<init>":()V
   4:   return

}


加了-O参数之后main()方法里啥也不剩了,只有个return。
这才是Java层的“优化”…

然则从Sun的JDK 1.3开始javac就忽略-O参数了。现在大家用JDK6里的javac就看不到这种效果。

================================================

换个例子,
public class Y {
  public static void main(String[] args) {
    String a = "alpha";
    String b = "beta";
    String c = "charlie";
    String d = a + b + c;
    System.out.println(d);
  }
}


还是用JDK 1.0.2里的javac,不带-O参数来编译:
Compiled from "Y.java"
public class Y extends java.lang.Object
  SourceFile: "Y.java"
  minor version: 3
  major version: 45
  Constant pool:
const #1 = String       #32;    //  alpha
const #2 = String       #22;    //  beta
const #3 = String       #45;    //  charlie
const #4 = class        #35;    //  java/lang/StringBuffer
const #5 = class        #36;    //  java/lang/Object
const #6 = class        #25;    //  java/io/PrintStream
const #7 = class        #24;    //  Y
const #8 = class        #42;    //  java/lang/System
const #9 = Method       #5.#18; //  java/lang/Object."<init>":()V
const #10 = Method      #4.#17; //  java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
const #11 = Field       #8.#15; //  java/lang/System.out:Ljava/io/PrintStream;
const #12 = Method      #4.#18; //  java/lang/StringBuffer."<init>":()V
const #13 = Method      #6.#16; //  java/io/PrintStream.println:(Ljava/lang/String;)V
const #14 = Method      #4.#19; //  java/lang/StringBuffer.toString:()Ljava/lang/String;
const #15 = NameAndType #33:#41;//  out:Ljava/io/PrintStream;
const #16 = NameAndType #20:#34;//  println:(Ljava/lang/String;)V
const #17 = NameAndType #44:#39;//  append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
const #18 = NameAndType #40:#46;//  "<init>":()V
const #19 = NameAndType #31:#21;//  toString:()Ljava/lang/String;
const #20 = Asciz       println;
const #21 = Asciz       ()Ljava/lang/String;;
const #22 = Asciz       beta;
const #23 = Asciz       ConstantValue;
const #24 = Asciz       Y;
const #25 = Asciz       java/io/PrintStream;
const #26 = Asciz       Exceptions;
const #27 = Asciz       LineNumberTable;
const #28 = Asciz       SourceFile;
const #29 = Asciz       LocalVariables;
const #30 = Asciz       Code;
const #31 = Asciz       toString;
const #32 = Asciz       alpha;
const #33 = Asciz       out;
const #34 = Asciz       (Ljava/lang/String;)V;
const #35 = Asciz       java/lang/StringBuffer;
const #36 = Asciz       java/lang/Object;
const #37 = Asciz       main;
const #38 = Asciz       ([Ljava/lang/String;)V;
const #39 = Asciz       (Ljava/lang/String;)Ljava/lang/StringBuffer;;
const #40 = Asciz       <init>;
const #41 = Asciz       Ljava/io/PrintStream;;
const #42 = Asciz       java/lang/System;
const #43 = Asciz       Y.java;
const #44 = Asciz       append;
const #45 = Asciz       charlie;
const #46 = Asciz       ()V;

{
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=5, Args_size=1
   0:   ldc     #1; //String alpha
   2:   astore_1
   3:   ldc     #2; //String beta
   5:   astore_2
   6:   ldc     #3; //String charlie
   8:   astore_3
   9:   new     #4; //class java/lang/StringBuffer
   12:  dup
   13:  invokespecial   #12; //Method java/lang/StringBuffer."<init>":()V
   16:  aload_1
   17:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   20:  aload_2
   21:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   24:  aload_3
   25:  invokevirtual   #10; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   28:  invokevirtual   #14; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
   31:  astore  4
   33:  getstatic       #11; //Field java/lang/System.out:Ljava/io/PrintStream;
   36:  aload   4
   38:  invokevirtual   #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   41:  return
  LineNumberTable:
   line 3: 0
   line 4: 3
   line 5: 6
   line 6: 9
   line 7: 33
   line 2: 41


public Y();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #9; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 1: 0


}


当时的Java就已经是用StringBuffer来实现字符串拼接(+)的了。
这也毫无出奇之处,在Java语言规范里就有这么写:
Java语言规范第一版 写道
15.17.1.2 Optimization of String Concatenation

An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class (§20.13) or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
For primitive objects, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.

顺带一提现在在JDK6里用的是Java语言规范第三版,而JDK7即将使用的是Java语言规范第四版(还没出)。

================================================

JVM的实现内部也有一些针对字符串拼接的优化的。但那些看Java字节码是看不出来的——在这个抽象层之下,屏蔽了“优化”这种细节。

例如说JRockit里有特制的jrockit.vm.StringMaker类专门用来优化字符串拼接,JRockit的JIT编译器会识别出StringBuilder的使用模式,发现符合条件的时候就把一些StringBuilder“悄悄的”转换为这种特别的StringMaker类来处理。

大家用得比较多的Oracle/Sun的HotSpot VM的比较新的版本里也有类似的优化,可以把符合条件的一些相邻的StringBuilder合并为一个,用于优化这种场景:
String a = x + y + z;
String b = a + x + y;

(假定变量a在后面就没有再被使用过了)
本来这段代码会被javac编译为等价于下面的代码:
String a = new StringBuilder().append(x).append(y).append(z).toString();
String b = new StringBuilder().append(a).append(x).append(y).toString();

而被HotSpot的JIT编译器优化后,会变成类似下面的样子:
String b = new StringBuilder().append(x).append(y).append(z).append(x).append(y).toString();

也就是把相邻的StringBuilder合并掉了。再次留意这个例子的前提是变量a在后面就没有被用过了,只有变量b在后面还有被用到。

控制这个优化的启动参数是OptimizeStringConcat,如果用Oracle JDK 6 update 21以上的版本的话可以试试对比开启它和关闭它的效果。

这才是“JVM做的优化”。字节码层面就能看出来的所谓“优化”根本还没到JVM那层。
分享到:
评论

相关推荐

    json_lib使用实例:java对象与json字符串的互转

    这个库提供了丰富的API,使得我们可以轻松地将Java对象转化为JSON字符串,以及将JSON字符串反序列化为Java对象。 ### JSON对象与Java对象的转换 1. **JSON字符串转Java对象** 使用`json-lib`,我们可以将JSON...

    JAVA日期与字符串的转换

    3. **性能考虑**:如果频繁进行日期和字符串之间的转换,可以考虑使用线程安全的`DateTimeFormatter`类(Java 8及以上版本提供)来替代`SimpleDateFormat`,以提高程序的性能。 4. **国际化支持**:如果应用程序需要...

    Java第6章 字符串 含源代码

    在Java编程语言中,字符串是极其重要且常用...通过学习这些源代码,开发者可以加深对Java字符串的理解,并提高编写高效字符串处理代码的能力。在实践中,理解并熟练掌握这些概念对于任何Java开发者来说都是至关重要的。

    java解析xml字符串

    通过以上分析,我们可以看到使用JDOM库在Java中解析和操作XML字符串的完整过程。这包括了如何将XML字符串转换为可读写的数据结构,以及如何遍历、读取和修改XML文档的元素和属性。这对于在Java应用程序中处理动态...

    Java获取随机字符串

    Java提供了多种方法来生成随机字符串,主要包括使用`java.util.Random`类和`java.security.SecureRandom`类。其中,`SecureRandom`类提供了一种更加安全的方式来生成随机数,它适合于安全性要求较高的应用场合。 ##...

    java对象转换为json字符串或字符串数组

    本文将深入探讨如何使用Java将对象转换为JSON以及字符串数组。 首先,我们需要一个库来处理JSON转换,这里提到的"json-lib"是一个广泛使用的Java JSON库,提供了多种类型到JSON的转换功能。在Java中,有两种主要...

    java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节;

    java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java 字符串转16进制 16进制转字符串 将两个ASCII字符合成一个字节; java ...

    Java 字符串常用方法

    - `new String()`: 使用此构造函数创建一个新的字符串对象,可以传入字符数组或另一个字符串作为参数。 - `""`: 空字符串字面量,表示没有字符的字符串。 2. **获取字符串信息** - `length()`: 返回字符串的长度...

    java字符串处理取出括号内的字符串

    在Java中,我们可以使用`java.util.regex`包中的`Pattern`和`Matcher`类来匹配和操作符合特定模式的字符串。对于提取括号内的内容,一个简单的正则表达式可以是`\((.*?)\)`,它匹配一对括号,并用`.*?`非贪婪地捕获...

    Java实现计算字符串表达式

    这篇博客“Java实现计算字符串表达式”可能讲解了如何利用Java来处理这种问题,虽然具体的实现细节没有提供,但我们可以基于一般的方法和常用的库来探讨这个主题。 首先,计算字符串表达式的基本步骤通常包括以下几...

    java 创建字符串类

    - **字符串连接**:可以使用`+`运算符或`StringBuilder`/`StringBuffer`类来连接字符串。但`+`运算符在处理大量字符串连接时效率较低,因为它会在每次连接时创建新的`String`对象。 2. **StringBuffer与...

    第4节: 揭秘JVM字符串常量池和Java堆-01

    第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: ...

    java 字符串工具类 java 字符串工具类

    java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java 字符串工具类java 字符串工具类 java ...

    实用小工具:Java实体类对比、Json对比、字符串动态拼接等功能

    标题中的“实用小工具:Java实体类对比、Json对比、字符串动态拼接等功能”涉及到的是在软件开发过程中常用的几个技术点,主要集中在Java后端和前端Vue.js的应用上。这里我们将详细探讨这些知识点: 1. **Java实体...

    java中截取带汉字的字符串

    ### Java中截取带汉字的字符串 在Java编程语言中,处理包含中文字符的字符串时,经常遇到的一个问题是如何正确地截取...通过以上方法,我们可以在Java中有效地处理包含中文字符的字符串截取问题,避免出现乱码等情况。

    java压缩字符串

    java压缩字符串

    移动字符串-java

    当需要对字符串进行修改时,可以使用字符数组。字符数组`char[]`允许我们逐个访问和修改元素。例如,我们可以创建一个字符数组来模拟字符串,并通过更新数组中的元素来“移动”字符串。 3. **控制台输出**: 在...

    java 分解字符串

    在Java编程语言中,分解字符串是一项常见的任务,它涉及到对字符串进行分析,将字符串分割成多个子字符串。这个过程通常被称为字符串分割。在Java中,我们主要使用`String`类提供的`split()`方法来实现这一功能。...

    java 字符串a-z排序

    在Java编程语言中,对字符串中的字符进行a到z排序是一项常见的操作,特别是在处理文本数据或需要对字母顺序排列的场景。本知识点将详细讲解如何实现这个功能。 首先,我们需要理解字符串在Java中的本质。在Java中,...

Global site tag (gtag.js) - Google Analytics