2

I have a question about how java deals with unused variables.

Let's say that i have the following code:

int notUsedVariable = aMethodThatExecutesSomethingImportantAndReturnsInt(someParameter);

Then I never use notUsedVariable in the code. Will that variable actually be stored, or java is smart enough to ignore the variable when compiling?

Thanks!

BMF
  • 307
  • 1
  • 5
  • 18
  • 3
    You cannot simply ignore the variable as the method may be modifying other parts of the object. It will definitely be compiled into bytecode, then the JIT compiler will optimize the bytecode depending on the runtime behaviour, – Extreme Coders Jun 19 '13 at 18:38
  • 2
    @ExtremeCoders, the way I read the question, @BMF is not asking whether `aMethodThatExecutesSomethingImportantAndReturnsInt` is invoked or not. The question is whether space is made in the thread's stack for the `notUsedVariable` int. – Dilum Ranatunga Jun 19 '13 at 18:46
  • Don't worry about it. If it's a function-local variable, the run-time storage cost in practice is virtually nothing, as the allocation would at worst consist of subtracting a slightly larger number from the CPU's stack pointer when entering the function. In fact, the JIT could say to itself that it's going to "store" it in the CPU register used for the called method's return value. Then the cost of storage is zero whether it does it or not! The worst cost is too small to benchmark but you could use [PrintAssembly](https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly) to examine it. – Boann Jun 19 '13 at 18:58
  • Where is `unusedVariable` declared? The answer depends a lot on this. e.g. If it's an instance variable, it can never be compiled away. – Henry Keiter Jun 19 '13 at 19:08

6 Answers6

5

My observation has been that javac may omit the store operations of unused variables if:

  1. The variables are marked as final and are initialized in the declaration;
  2. You do not compile with local variable debug info (-g:vars)

If you compile with -g:vars, javac will keep the variables loads and stores in tact for debugging purposes. It does not appear to consider non-final variables as eligible for removal.

Test for yourself. My results with JDK 7 are below. The results were identical with a JDK 8 EAP.

Input:

class MyTest {
    public static void main(String... args) {
        int a = 1;
    }
}

Output:

public static void main(java.lang.String... p0);
  Flags: PUBLIC, STATIC, VARARGS
  Code:
    stack=1, locals=2, arguments=1
         0: iconst_1
         1: istore_1
         2: return

Input:

class MyTest {
    public static void main(String... args) {
        final int a = 1;
    }
}

Output:

public static void main(java.lang.String... p0);
  Flags: PUBLIC, STATIC, VARARGS
  Code:
    stack=1, locals=2, arguments=1
         0: return

As others have said, in either case I would expect the JIT optimizer to omit any unnecessary store operations.

Mike Strobel
  • 25,075
  • 57
  • 69
  • 1
    Forgot to mention that the `final` variable must be initialized in the declaration. If you were to write `final int a; a = 1;`, the variable would not be optimized away. – Mike Strobel Jun 19 '13 at 19:38
  • the main difference is that `a`, in `final int a = 1;`, is a constant variable whereas in `final int a; a = 1;` it is not. – assylias Jun 19 '13 at 19:39
  • Well, `a` is constant in either case by the definite assignment analysis rules laid out in the spec. But javac probably doesn't do full DAA when determining whether it can safely remove a variable. It probably checks the `HASINIT` flag that the compiler sets during an earlier pass, resulting in a cheaper but stricter optimization. – Mike Strobel Jun 19 '13 at 19:42
  • 1
    Actually it is not a constant in the second example. See the last paragraphs of http://stackoverflow.com/a/13235726/829571. In particular, it needs to be final **and** initialized with a compile-time constant expression, which is not the case (it is not initialized in the declaration statement). – assylias Jun 19 '13 at 19:55
  • 1
    D'oh, terminology fail on my part. Touché. It is final, and the value is guaranteed to be set exactly once (i.e., "constant"), but it is not recognized as a _constant variable_. – Mike Strobel Jun 19 '13 at 20:10
3

It depends.

If notUsedVariable is a local variable, the assignment will probably be ignored by the JIT compiler (but we are talking about one register read/write, i.e. sub-nanosecond stuff on modern desktop processors). As demonstrated by MattBall the bytecode will keep the assignment.

If notUsedVariable is a member of the class, the result will need to be stored as the field might be accessed later on and it would probably be impossible for the compiler to prove otherwise (a new class could be loaded that does not exist yet for example).

assylias
  • 321,522
  • 82
  • 660
  • 783
2

If you're concerned just about the static compilation step, and not the JIT, this is simple to check by comparing the bytecode generated from two slightly different classes, using javap:

class WithLocalVar {

    private static int methodWithSideEffect() {
        System.out.println();
        return 42;
    }

    public static void main(String[] args) {
        int result = methodWithSideEffect();
    }
}

class WithoutLocalVar {

    private static int methodWithSideEffect() {
        System.out.println();
        return 42;
    }

    public static void main(String[] args) {
        methodWithSideEffect();
    }
}

✗  javac With*
✗  javap -c WithLocalVar
Compiled from "WithLocalVar.java"
class WithLocalVar extends java.lang.Object{
WithLocalVar();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #4; //Method methodWithSideEffect:()I
   3:   istore_1
   4:   return

}

✗  javap -c WithoutLocalVar
Compiled from "WithoutLocalVar.java"
class WithoutLocalVar extends java.lang.Object{
WithoutLocalVar();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #4; //Method methodWithSideEffect:()I
   3:   pop
   4:   return

}

Therefore, no, the compiler won't optimize away the istore_1. The JIT is another story...

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
2

javac does not perform many optimizations. The JIT, on the other hand, does

Check out http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/

Here is a qoute.

More importantly, the javac compiler does not perform simple optimizations like loop unrolling, algebraic simplification, strength reduction, and others. To get these benefits and other simple optimizations, the programmer must perform them on the Java source code and not rely on the javac compiler to perform them.

There is also another thread going into more detail about this. Optimization by Java Compiler

Community
  • 1
  • 1
Frank Sposaro
  • 8,511
  • 4
  • 43
  • 64
  • 2
    *"To get these benefits and other simple optimizations, the programmer must perform them on the Java source code and not rely on the javac compiler to perform them."* is quite misleading. The programmer should instead rely on the JIT to perform these optimisations. In other words, don't start unrolling your loops manually! – assylias Jun 19 '13 at 18:58
2

Let's compile an example

public class Test {
   public static void main(String... args) {
       int a = 1;
   }
}

We get

public class Test {
  public Test();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String...);
    Code:
       0: iconst_1       // push integer 1 on stack      
       1: istore_1       // pop integer from stack, store it in local variable 1
       2: return        
}

We can see that local variable was not removed. It was stored.

Please remember that at the time of execution optimizations may happen.

Grzegorz Żur
  • 47,257
  • 14
  • 109
  • 105
  • 3
    It's important to point out that this applies only to bytecode compilation, whose details have little effect on the runtime execution of the code on modern JVMs. – Boann Jun 19 '13 at 18:49
  • 2
    The results also depend on the level of debug info generated by the compiler (i.e., whether `-g:vars` is specified) and whether the variable is marked as `final`. See my answer for the differences. – Mike Strobel Jun 19 '13 at 19:21
0

It will be stored based upon the scope of the variable that when the scope ends, garbage collection will clean up the memory used by the variable.

I edited once of my Test classes for a class variable and local variable and then used Eclipse to inspect the class file. (Eclipse complains that the variable is never used.)

// Compiled from UserLoadTest.java (version 1.6 : 50.0, super bit)
public class org.dev.user.UserLoadTest extends org.test.BaseTestCase {

  // Field descriptor #6 I
  public int myVariable;

...we can see that the class file sees this

@org.junit.Test
  public void testBasicUserLoad() throws java.io.IOException, org.custommonkey.xmlunit.exceptions.XpathException;
      0  aload_0 [this]
      1  ldc <String "user_01.xml"> [24]
...
org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo(java.lang.String, java.lang.String, org.w3c.dom.Document) : void [85]
    223  aload_2 [reloaded]
    224  invokevirtual org.testHarness.Result.getDocument() : org.dom4j.dom.DOMDocument [81]
    227  astore_3 [d]
    228  return

line 224 is a simple declaration of a variable using

    Document d = reloaded.getDocument();

This does nothing with d but the class file recognizes that the variable was created.

iowatiger08
  • 1,892
  • 24
  • 30
  • I read this a bit hastily and see you mentioned the compiler. During runtime, the variable will be stored until garbage collection. You can look at the byte code of class files and see references stored for variables declared regardless if they are used. – iowatiger08 Jun 19 '13 at 18:53