Saturday, September 26, 2009

Java String, StringBuilder and StringBuffer

I had a phone conversation with someone yesterday afternoon about Java's String and StringBuffer and how they work. After 24 hours had elapsed my thoughts on String still comes back to bug me - as if I missed out something.

The urge to scratch this itch finally made me fire up “vi” from my terminal. Yes I know what you are thinking, I'm no "vi" expert, but do I really need Eclipse to write 15 lines of code? :)

Here is the source:

1:  public class Str {  
2: public void sayHelloWorld() {
3: String str1 = "hello " + "world ";
4: String str2 = "hello world again";
5: }
6: public void sayGoodbyeWorld() {
7: String str3 = "goodbye world " + 3;
8: }
9: public void sayYouSayMe() {
10: String str4 = "";
11: for (int i = 0; i < 3; i++) {
12: str4 += "say you say me ";
13: }
14: }
15: }


Compiling the source with JDK 6 followed by
“javap -c Str" gives us:

1:  Compiled from "Str.java"  
2: public class Str extends java.lang.Object{
3: public Str();
4: Code:
5: 0: aload_0
6: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
7: 4: return
8: public void sayHelloWorld();
9: Code:
10: 0: ldc #2; //String hello world
11: 2: astore_1
12: 3: ldc #3; //String hello world again
13: 5: astore_2
14: 6: return
15: public void sayGoodbyeWorld();
16: Code:
17: 0: ldc #4; //String goodbye world 3
18: 2: astore_1
19: 3: return
20: public void sayYouSayMe();
21: Code:
22: 0: ldc #5; //String
23: 2: astore_1
24: 3: iconst_0
25: 4: istore_2
26: 5: iload_2
27: 6: iconst_3
28: 7: if_icmpge 36
29: 10: new #6; //class java/lang/StringBuilder
30: 13: dup
31: 14: invokespecial #7; //Method java/lang/StringBuilder."<init>":()V
32: 17: aload_1
33: 18: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: 21: ldc #9; //String say you say me
35: 23: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: 26: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: 29: astore_1
38: 30: iinc 2, 1
39: 33: goto 5
40: 36: return
41: }


The disassembled JDK 6 code from above reveals quite a bit about this simple class.
  • Line 2: the compiler automatically extends our class from java.lang.Object.

  • Line 3-7: the compiler automatically inserts a default Str constructor that invokes java.lang.Object constructor

  • Line 10: ldc #2 suggests that “hello world” string is stored as the 2nd item from constant pool table, where “hello " and "world” is automagically concatenated for you by the compiler

  • Line 12: ldc #3 suggests that “hello world again” string is stored as the 3rd item from constant pool table

  • Line 17: ldc #4 suggests that “goodbye world 3” is stored as the 4th item from constant pool table, where the compiler again automagically concatenated integer “3” to "goodbye world "

  • Line 22: ldc #5 suggests that our empty string is stored as the 5th item from constant pool table

  • Line 29-36: tells us that javac inserts StringBuilder to concatenate "say you say me " strings with "say you say me " stored as the 9th item from constant pool table

  • Line 36: StringBuilder object invokes the toString method and assigns it back to “str4”





Let's try compiling the same source with JDK 1.4 by running “javac -source 1.4 -target 1.4 Str.java” then followed by “javap -c Str” which gives us:

1:  Compiled from "Str.java"  
2: public class Str extends java.lang.Object{
3: public Str();
4: Code:
5: 0: aload_0
6: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
7: 4: return
8: public void sayHelloWorld();
9: Code:
10: 0: ldc #2; //String hello world
11: 2: astore_1
12: 3: ldc #3; //String hello world again
13: 5: astore_2
14: 6: return
15: public void sayGoodbyeWorld();
16: Code:
17: 0: ldc #4; //String goodbye world 3
18: 2: astore_1
19: 3: return
20: public void sayYouSayMe();
21: Code:
22: 0: ldc #5; //String
23: 2: astore_1
24: 3: iconst_0
25: 4: istore_2
26: 5: iload_2
27: 6: iconst_3
28: 7: if_icmpge 36
29: 10: new #6; //class java/lang/StringBuffer
30: 13: dup
31: 14: invokespecial #7; //Method java/lang/StringBuffer."<init>":()V
32: 17: aload_1
33: 18: invokevirtual #8; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
34: 21: ldc #9; //String say you say me
35: 23: invokevirtual #8; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
36: 26: invokevirtual #10; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
37: 29: astore_1
38: 30: iinc 2, 1
39: 33: goto 5
40: 36: return
41: }



The disassembled code for 1.4 and 1.6 looks the same except that 1.4 uses StringBuffer instead of StringBuilder as StringBuilder was introduced as part of JDK 5 and later. So it looks like a little bit of String optimisation is done on the Javac level not on the JVM level.

No comments:

Post a Comment