Reputation:
In the following code, When it reaches the comment, if the GC is not run, approximately 1000 object is created(according to OCA book), the StringBuilder
is modified and remains as one object, the empty string " "
is pooled and re-used, that's all that is explained. isn't the argument s
a new String("s")
that needs to be GCed, and i
, will it not be converted to a new String
object first, then combined with " "
creates another new String
object making them 2 String objects at that line, eligible for GC along with the append
's argument, a total of 3 String object in every loop. so a sum of 3000 object when the code reaches the comment line?
public class Mounds {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
String s = new String();
for (int i = 0; i < 1000; i++) {
s = " " + i;
sb.append(s);
}
// done with loop
}
}
Upvotes: 1
Views: 1517
Reputation: 109557
The optimal usage would be:
StringBuilder sb = new StringBuilder(4000);
for (int i = 0; i < 1000; ++i) {
sb.append(' ').append(i);
}
... do something with sb.toString()
As:
String s = new String();
creates an unnecessary empty string. Same as String s = "";
. (Optimization not considered.)s = " " + i;
concatenates two strings into a new String. A task one should leave to the StringBuilder, as that is the sole purpose of it.' '
is more efficient than a String " "
.new StringBuilder(4000)
with an initial capacity one could use here, preventing intermittent reallocation on appending. 1000 numbers of which 900 are 3 digits, plus a space, will fit in 4000 chars.Upvotes: 1
Reputation: 741
If we compile this code and look at the generated bytecode we can examine that exactly
public static void main(java.lang.String[]) throws java.io.IOException;
Code:
0: new #19 // class java/lang/StringBuilder
3: dup
4: invokespecial #21 // Method java/lang/StringBuilder."<init>":()V
7: astore_1
8: new #22 // class java/lang/String
11: dup
12: invokespecial #24 // Method java/lang/String."<init>":()V
15: astore_2
16: iconst_0
17: istore_3
18: goto 47
21: new #19 // class java/lang/StringBuilder
24: dup
25: ldc #25 // String
27: invokespecial #27 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
30: iload_3
31: invokevirtual #30 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
34: invokevirtual #34 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: astore_2
38: aload_1
39: aload_2
40: invokevirtual #38 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: pop
44: iinc 3, 1
47: iload_3
48: sipush 1000
51: if_icmplt 21
54: return
The instructions we care about are from 21 to 40. In 21, there is a second StringBuilder created, we will come back to that later.
In 25 We see, that there is a ldc, which means that a literal is pushed to the stack, in this case it is the literal String " ".
Then the real magic happens. The constructor of the second StringBuilder is called, which takes the literal from the stack as argument. Then the int i is loaded from the local variable array with iload_3, after that the append method of the second StringBuilder is called to append that i to it, and then the toString is called. With astore_2 and aload_1 the return value of the toString call is stored, and the first StringBuilder is loaded, and after that the String is loaded again. And finally the append method of the first StringBuilder is called to add that new String to the StringBuilder.
So it turns out, that ther is a new StringBuilder created in every loop, because everytime you use " " + i a StringBuilder has to be created to concatenate the String and int. Additionally a new String will be created by the toString method of the intermediate StringBuilder, so there will be a total of 2000 Objects there.
A better version would look like this:
for (int i = 0; i < 1000; i++) {
sb.append(' ');
sb.append(i);
}
That will create the following bytecode:
public static void main(java.lang.String[]) throws java.io.IOException;
Code:
0: new #19 // class java/lang/StringBuilder
3: dup
4: invokespecial #21 // Method java/lang/StringBuilder."<init>":()V
7: astore_1
8: new #22 // class java/lang/String
11: dup
12: invokespecial #24 // Method java/lang/String."<init>":()V
15: astore_2
16: iconst_0
17: istore_3
18: goto 37
21: aload_1
22: bipush 32
24: invokevirtual #25 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
27: pop
28: aload_1
29: iload_3
30: invokevirtual #29 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
33: pop
34: iinc 3, 1
37: iload_3
38: sipush 1000
41: if_icmplt 21
44: return
We can see, that there now is only one StringBuilder, which gets its append method called twice, so no memory is allocated here and this should be better.
Upvotes: 4
Reputation: 14328
the compiler probably realises that the scope of variable s
is inside the loop so it inlines the assignment into the append()
to produce
sb.append(" " + i)
so now only the conversion of the int creates a new String in each iteration.
Upvotes: -1