我对java如何处理未使用的变量有疑问。
假设我有以下代码:
int notUsedVariable = aMethodThatExecutesSomethingImportantAndReturnsInt(someParameter);
然后我从不在代码中使用notUsedVariable。 该变量是否实际存储,或者java是否足够聪明,在编译时忽略变量?
谢谢!
答案 0 :(得分:5)
我的观察是javac可能会忽略未使用变量的存储操作如果:
final
,在声明中初始化; -g:vars
)进行不编译如果使用-g:vars
进行编译,javac将保持变量加载和存储以便进行调试。它似乎不认为非最终变量有资格被删除。
自己测试。我在JDK 7的结果如下。结果与JDK 8 EAP相同。
输入:
class MyTest {
public static void main(String... args) {
int a = 1;
}
}
输出:
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
输入:
class MyTest {
public static void main(String... args) {
final int a = 1;
}
}
输出:
public static void main(java.lang.String... p0);
Flags: PUBLIC, STATIC, VARARGS
Code:
stack=1, locals=2, arguments=1
0: return
正如其他人所说,在任何一种情况下,我都希望JIT优化器省略任何不必要的商店操作。
答案 1 :(得分:3)
取决于。
如果notUsedVariable
是局部变量,JIT编译器可能会忽略该赋值(但我们讨论的是一个寄存器读/写,即现代桌面处理器上的亚纳秒内容)。如MattBall所示,字节码将保留赋值。
如果notUsedVariable
是该类的成员,则需要存储结果,因为稍后可能会访问该字段,编译器可能无法证明其他情况(可以加载新类)例如,这还不存在。)
答案 2 :(得分:2)
如果您只关注 关于静态编译步骤,而不是the JIT,那么通过比较使用{{3}从两个稍微不同的类生成的字节码来检查是很简单的。 }}:
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
}
因此,不,编译器不会优化istore_1
。 javap
答案 3 :(得分:2)
javac不执行许多优化。另一方面,JIT确实
查看http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/
这是一个qoute。
更重要的是,javac编译器执行起来并不简单 循环展开,代数简化,强度等优化 减少等等。获得这些好处和其他简单 优化,程序员必须在Java源代码上执行它们 代码而不依赖于javac编译器来执行它们。
还有另一个关于此问题的更多细节。 Optimization by Java Compiler
答案 4 :(得分:2)
让我们编译一个例子
public class Test {
public static void main(String... args) {
int a = 1;
}
}
我们得到了
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
}
我们可以看到没有删除局部变量。 已存储。
请记住,在执行时可能会发生优化。
答案 5 :(得分:0)
它将根据变量的范围进行存储,当范围结束时,垃圾收集将清理变量使用的内存。
我为我的Test类编辑了一次类变量和局部变量,然后使用Eclipse来检查类文件。 (Eclipse抱怨从未使用过该变量。)
// 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;
...我们可以看到类文件看到了这个
@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
第224行是使用
的变量的简单声明 Document d = reloaded.getDocument();
这对d
没有任何作用,但是类文件识别出变量已创建。