java中变量的内存分配

时间:2013-06-19 18:35:16

标签: java memory jvm

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

假设我有以下代码:

int notUsedVariable = aMethodThatExecutesSomethingImportantAndReturnsInt(someParameter);

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

谢谢!

6 个答案:

答案 0 :(得分:5)

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

  1. 变量标记为final 在声明中初始化;
  2. 您使用本地变量调试信息(-g:vars)进行编译
  3. 如果使用-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_1javap

答案 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没有任何作用,但是类文件识别出变量已创建。