it'll just allocate a reference to a String object on stack every iteration.
That's not how it works. Variables on the stack, i.e. parameters and local variables, are allocated (reserved) on method entry.
E.g. if you have code like this:
static void foo() {
String s;
for (int i = 0; i < 5; i++) {
int j = i;
s = String.valueOf(j);
bar(s);
}
for (int j = 0; j < 5; j++) {
int k = j;
s = String.valueOf(k);
bar(s);
}
}
static void bar(String s) {
}
For that code, 3 slots1 will be allocated on the stack:
s
will be in slot 0, and use the slow for the entire method
i
will be in slot 1, for the duration of the first loop.
j
will be in slot 2, for the duration of the body of the first loop.
Another j
will be in slot 1, for the duration of the second loop.
k
will be in slot 2, for the duration of the body of the second loop.
As you can see, slots 1 and 2 are reused, but there is no "allocation" going on during the method execution. Only the memory allocated/reversed at the beginning of the method.
1) A slot is 4 bytes / 32 bits, i.e. the size of an int
or a reference (with compressed Oops).
If you compile with javac -g Test.java
and disassemble with javap -v -c Test.class
, you get (output from with Java 8):
static void foo();
descriptor: ()V
flags: ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 24
7: iload_1
8: istore_2
9: iload_2
10: invokestatic #2 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
13: astore_0
14: aload_0
15: invokestatic #3 // Method bar:(Ljava/lang/String;)V
18: iinc 1, 1
21: goto 2
24: iconst_0
25: istore_1
26: iload_1
27: iconst_5
28: if_icmpge 48
31: iload_1
32: istore_2
33: iload_2
34: invokestatic #2 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
37: astore_0
38: aload_0
39: invokestatic #3 // Method bar:(Ljava/lang/String;)V
42: iinc 1, 1
45: goto 26
48: return
LocalVariableTable:
Start Length Slot Name Signature
9 9 2 j I
14 10 0 s Ljava/lang/String;
2 22 1 i I
33 9 2 k I
38 10 0 s Ljava/lang/String;
26 22 1 j I
As you can see, the line stack=2, locals=3, args_size=0
shows that it will reserve 3 slots for local variables. The LocalVariableTable
at the bottom shows which local variable uses which slot for the duration of which bytecode instructions (scope).
Moving the declaration of s
inside the loops will rearrange the order in which variables are assigned to slots, i.e. which slots they use, and changes the length of the scope of s
, but that's it.
static void foo() {
for (int i = 0; i < 5; i++) {
int j = i;
String s = String.valueOf(j);
bar(s);
}
for (int j = 0; j < 5; j++) {
int k = j;
String s = String.valueOf(k);
bar(s);
}
}
LocalVariableTable:
Start Length Slot Name Signature
9 9 1 j I
14 4 2 s Ljava/lang/String;
2 22 0 i I
33 9 1 k I
38 4 2 s Ljava/lang/String;
26 22 0 j I