Impact Of Local Variable Declaration Location

2013-01-02 05:20UTC

Does the location of a local variable's declaration result in performance changes of a method? For example, will a String variable, s, declared outside of a loop exhibit "better" performance than when declared inside a loop? In Java, looking at a class' bytecode explains, irrefutably, why the answer is "no".

The Source

The following example demonstrates the setup: declaration of variable, s, both inside and outside a loop. All developers should agree that the methods below, inner() and outer(), perform the same work: loop 10 times and write the counter to a local String variable:

public final class Test
{
    public final void inner()
    {
        for(int i = 0; i < 10; i++) {
            final String s = Integer.toString(i);
        }
    }

    public final void outer()
    {
        int i;
        String s;
        for(i = 0; i < 10; i++) {
            s = Integer.toString(i);
        }
    }
}

In truth, the above methods are theoretically useless: Integer.toString() shouldn't have side effects and the two methods may thus be eliminated. Further, the code formatting of outer() is slightly irregular and inconsistent with inner(); however, I have done this for demonstrative purposes.

The Bytecode

The following block shows the bytecode, as generated by javap, for both methods:

public final void inner();
  Code:
     0: iconst_0
     1: istore_1
     2: iload_1
     3: bipush        10
     5: if_icmpge     19
     8: iload_1
     9: invokestatic  #2                  // Method java/lang/Integer.toString:(I)Ljava/lang/String;
    12: astore_2
    13: iinc          1, 1
    16: goto          2
    19: return
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
       13       0     2     s   Ljava/lang/String;
        2      17     1     i   I
        0      20     0  this   LTest;

public final void outer();
  Code:
     0: iconst_0
     1: istore_1
     2: iload_1
     3: bipush        10
     5: if_icmpge     19
     8: iload_1
     9: invokestatic  #2                  // Method java/lang/Integer.toString:(I)Ljava/lang/String;
    12: astore_2
    13: iinc          1, 1
    16: goto          2
    19: return
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
       13       6     2     s   Ljava/lang/String;
        0      20     0  this   LTest;
        2      18     1     i   I

Notice how the bytecode for inner() and outer() is identical: s is stored in slot 2 (index 12) of the local variable table and i in slot 1 (index 1). Importantly, this demonstrates that local variables are not naïvely interpreted on a per-line basis but are provisioned during bytecode compilation. This means there cannot be, nor is there, any reasonable difference in performance due to the location of a declaration of a local variable. For interested readers, the JVM documentation provides excellent insight into local variables.

For some reason, this question remains a hot topic on numerous Internet forums. I feel the need to point out this bytecode is not a special case for String types, as the JVM operates on only 2 types: reference and primitive. In this case, as identified by the astore operation, s is a reference type to a heap-bound object instance. Specifically, s is not a mystical JVM string type. All objects, complex or not, are handled in the exact same way: as reference types.

Considerations

In a non-technical way, I argue a variable should be constrained to the smallest scope possible. Keeping a variable hidden when out of scope communicates to other developers, without out-of-band comments and documentation, your pre-conditions and post-conditions of a code block. In addition, using constants (final) instead of variables further increases such understanding and communication. In any case, I strongly urge fellow developers to use the smallest possible scope for variables or constants, favouring constants whenever possible.