2

我对 java 如何处理未使用的变量有疑问。

假设我有以下代码:

int notUsedVariable = aMethodThatExecutesSomethingImportantAndReturnsInt(someParameter);

然后我从不在代码中使用 notUsedVariable。是否会实际存储该变量,或者java足够聪明以在编译时忽略该变量?

谢谢!

4

6 回答 6

5

我的观察是 javac 可能会省略未使用变量的存储操作,如果

  1. 变量被标记为final 在声明中初始化;
  2. 使用局部变量调试信息 ( -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 优化器省略任何不必要的存储操作。

于 2013-06-19T19:16:25.253 回答
3

这取决于。

如果notUsedVariable是一个局部变量,JIT 编译器可能会忽略该赋值(但我们谈论的是一个寄存器读/写,即现代桌面处理器上的亚纳秒级的东西)。正如 MattBall 所证明的,字节码将保留分配。

如果notUsedVariable是该类的成员,则需要存储结果,因为稍后可能会访问该字段,并且编译器可能无法证明其他情况(例如,可以加载一个尚不存在的新类) .

于 2013-06-19T18:42:53.693 回答
2

如果您关心静态编译步骤,而不是JIT,这很容易通过比较从两个稍微不同的类生成的字节码来检查,使用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

}

因此,不,编译器不会优化掉istore_1. JIT 是另一个故事……

于 2013-06-19T18:44:28.503 回答
2

javac 不执行很多优化。另一方面,JIT 确实

查看http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/

这是一个qoute。

更重要的是,javac 编译器不执行简单的优化,如循环展开、代数简化、强度降低等。为了获得这些好处和其他简单的优化,程序员必须在 Java 源代码上执行它们,而不是依赖 javac 编译器来执行它们。

还有另一个线程对此进行了更详细的介绍。 Java编译器优化

于 2013-06-19T18:45:45.097 回答
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        
}

我们可以看到局部变量没有被删除。它被存储了

请记住,在执行时可能会发生优化。

于 2013-06-19T18:46:15.423 回答
0

它将基于变量的范围进行存储,当范围结束时,垃圾收集将清理变量使用的内存。

我为类变量和局部变量编辑了一次我的测试类,然后使用 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类文件无关,但会识别该变量已创建。

于 2013-06-19T18:40:09.790 回答