0

Let's look at this example:

String var;
while (...) {
        var = ...
        // do stuff
}

In this case, we create a reference to a String object and assign different objects to it in each iteration of the loop.

Now, in this other example:

while (...) {
        String var = ...
        // do stuff
}

If we're assuming the compiler is naive, it'll just allocate a reference to a String object on stack every iteration.

Or will it? That is my question - does the (a?) Java compiler perform this optimization? I always leave object declarations in the widest scope possible because I am worried about this, but if the compiler does this already it's one less pebble on my shoe.

Thank you in advance!

3
  • 5
    The JIT will optimize it, Java has two compilation phases; the byte code compiler and then the JIT. Commented Mar 2, 2020 at 1:27
  • @Elliott mind elaborating? Thank you for the reply! Commented Mar 2, 2020 at 1:59
  • The JIT won't optimize it, as there is nothing to optimize. It doesn't do either of these things. The entire stack frame is allocated on entry to the function. If you examine the bytecode for these two examples you will see it's identical. @ElliottFrisch Commented Mar 2, 2020 at 2:53

1 Answer 1

6

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
Sign up to request clarification or add additional context in comments.

2 Comments

Oops, once again I created a duplicate answer: stackoverflow.com/a/34341363/5221149
excellent explanation, that was exactly what I was looking for! Unfortunately I have spent all my daily upvotes, but I'll be sure to return and +1 the answer :). Cheers!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.