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!
6 Answers 6
My observation has been that javac may omit the store operations of unused variables if:
- The variables are marked as
finaland are initialized in the declaration; - 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.
5 Comments
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.a, in final int a = 1;, is a constant variable whereas in final int a; a = 1; it is not.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.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).
Comments
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
1 Comment
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...
Comments
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.
2 Comments
-g:vars is specified) and whether the variable is marked as final. See my answer for the differences.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.
aMethodThatExecutesSomethingImportantAndReturnsIntis invoked or not. The question is whether space is made in the thread's stack for thenotUsedVariableint.unusedVariabledeclared? The answer depends a lot on this. e.g. If it's an instance variable, it can never be compiled away.