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!

asked Jun 19, 2013 at 18:35
4
  • 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, Commented Jun 19, 2013 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. Commented Jun 19, 2013 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 to examine it. Commented Jun 19, 2013 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. Commented Jun 19, 2013 at 19:08

6 Answers 6

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.

answered Jun 19, 2013 at 19:16
Sign up to request clarification or add additional context in comments.

5 Comments

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.
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.
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.
Actually it is not a constant in the second example. See the last paragraphs of 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).
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.
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).

answered Jun 19, 2013 at 18:42

Comments

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

answered Jun 19, 2013 at 18:45

1 Comment

"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!
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...

answered Jun 19, 2013 at 18:44

Comments

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.

answered Jun 19, 2013 at 18:46

2 Comments

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.
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.
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.

answered Jun 19, 2013 at 18:40

1 Comment

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.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.