31

I came across this post in SO Do uninitialized primitive instance variables use memory?

It states "In Java, does it cost memory to declare a class level instance variable without initializing it? For example: Does int i; use any memory if I don't initialize it with i = 5;?"

My question is what in case of local variables, say i have a method foo()

public int foo(){

  int x;

//Write code which does not use/initialize x
}

Will the local variable x occupy memory?

Edit

Jon's Answer is

UPDATE: Doing a bit more research on this, I find this page which suggests to me that, although the compiled bytecode implies that space is allocated for x, it may indeed be optimized away by the jvm. Unfortunately, I find no complete description of the optimizations performed. Particularly, the JVM documentation chapter on compiling does not mention removing unused variables from the stack. So, barring further discoveries, my answer would be that it's implementation-dependent, but it seems like the sort of optimization that any self-respecting compiler would perform. Notice too that it doesn't matter that much that this is a local variable rather than a field - in fact, local variables are the ones most likely to be optimized away, since they are the easiest to analyze and eliminate. (precisely because they are local)

Let us see if can find more evidences which supports this.

Community
  • 1
  • 1
Abhishek Singh
  • 10,243
  • 22
  • 74
  • 108
  • 2
    Unused variables would be optimized out by the compiler. – Dave Newton Oct 29 '14 at 14:26
  • Java JIT in the JVM will optimise these unused variables in all cases. – Subhrajyoti Majumder Oct 29 '14 at 14:29
  • In some circumstances the space in the automatic stack frame will be allocated. But the cost to allocate 4-8 bytes of stack frame space is vanishingly small. – Hot Licks Oct 29 '14 at 20:09
  • I'm not sure it's of much value looking at the bye code, as people have pointed out. The grunt work is done by the JVM, so the assembly is what we want to see (I tried but [getting assembly](http://www.ashishpaliwal.com/blog/2013/05/jvm-how-to-see-assembly-code-for-your-java-program/) from a 64bit jvm is a absolute [ballache](http://jpbempel.blogspot.co.uk/2013/07/how-to-build-hsdis-amd64dll.html)). I would chortle and say that seeing the assembly is so much easier in .NET, but then I remembered I have express installed here and isn't that just it's own can of worms. – Nathan Cooper Oct 29 '14 at 23:51
  • I am confused by jon and The Lost Mind's answer. They appear contradictory to me. Correct me if i am wrong. – Abhishek Singh Oct 30 '14 at 06:04
  • @NathanCooper - Remember that the bytecodes are directly interpreted by the JVM interpreter, so the stack frame layout is pretty well defined by what you see. The JITC may then *optionally* translate the bytecodes into machine language if the method is "hot" enough to merit it, but the starting assumption is that the interpreter is interpreting. And what individual JITCs do with the stack frame layout is all over the map -- some may use it essentially unchanged while others may register-map everything and essentially do away with "local" storage. – Hot Licks Oct 31 '14 at 18:33
  • @HotLicks Good point. But to the extent this question is answerable it would be nice to have a look. – Nathan Cooper Oct 31 '14 at 19:03
  • @NathanCooper - Have a look at what? If you want to go to all the trouble to force a JITC to generate code, capture the code, and then try to make heads or tails of it, be my guest. – Hot Licks Oct 31 '14 at 19:06
  • 1
    @AbhishekSingh - The difference in our answers reflects the fact that Java does most of its heavy optimization in the JIT, but that optimization is not well specified and can be implementation dependent. See my updated answer for a frustratingly inconclusive conclusion. – Jon Kiparsky Nov 21 '14 at 05:36
  • @JonKiparsky bounty expires today. Your answer seems to be more convincing. I dont know if i am right,but i will mark it as a answer. – Abhishek Singh Nov 21 '14 at 06:52

5 Answers5

34

Class level / Instance level variables will be initialized to their default values automatically. So, yes, they will occupy some space when a class is initialized / instance created respectively.

As far as method local variables are concerned, No, if they are just declared but not initialized, then they will not use any space, they are as good as ignored by the compiler..

If your code was this :

public static void main(String[] args) {
    int i;  // ignored
    int j = 5;
    String s = "abc";
    String sNull; // ignored
}

Byte code :

  LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  args   [Ljava/lang/String;
            2       4     2     j   I
            5       1     3     s   Ljava/lang/String;
   }
TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • 3
    Maybe worth pointing out that technically, the answer is compiler/JVM specific but in practice what you said. – assylias Oct 29 '14 at 14:49
  • 3
    Afaik the JIT will get rid of dead code and will even remove the 'int j=5'. So there is not really much point looking at the byte-code; you should look at the assembly. – pveentjer Oct 29 '14 at 16:46
  • @pveentjer - That is not guaranteed to happen on all JVMs. Yes, this will happen on some JVMs but this statement is still, *implementation dependent*. The JVM specification doesn't specify this to be mandatory. :) – TheLostMind Oct 29 '14 at 17:40
  • 1
    @pveentjer - Also, there is a very good use of looking at byte-code. It is showing that the compiler is ignoring uninitialized method level variables. – TheLostMind Oct 29 '14 at 17:42
  • 4
    You will notice that slot 1 is skipped. This is where `i` is located. So it is, at least conceptually, taking space. – Hot Licks Oct 29 '14 at 20:12
  • @HotLicks - Then why isn't it showing up on the table?.. My understanding is that the compiler *parses* i but ignores it.. It puts i in the table as soon as it is encountered.. Then once it finds out that `i` isn't being used, it removes it but doesn't *update* the table. I might be wrong... But this is what I think is happening – TheLostMind Oct 29 '14 at 20:49
  • Because it's not used, and hence has no type. The type of a var is implied by its use, and, in fact, a single stack slot may be used for different variables at different points in a method. – Hot Licks Oct 29 '14 at 20:53
  • The compiler allocates a slot for `i` as soon as it sees it. It doesn't remove the slot because (a) there's no real need to do this level of "optimization", and (b) subsequent variables would then have their slot indices altered, making a minor mess. See Jon's answer below for further evidence that the slot is actually allocated. – Hot Licks Oct 29 '14 at 20:56
  • @TheLostMind i have question why Jon Kiparsky said "So it looks like space is indeed allocated for x, even though the compiler output doesn't ever use it." why he got up vote? plz make it clear for me thanks – Mohsin AR Jun 17 '15 at 06:28
19

This is the kind of question that's worth examining with javap.

public class Foo
{
public int bar(){

  System.out.println("foo");
    return 8;
  }
public int foo(){

  int x;
  System.out.println("foo");
    return 8;
  }
}

Notice that the difference between foo() and bar() is that one declares a local variable x and the other does not.

Now look at the jvm code (use javap -v Foo to see this on your machine)

  public int bar();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String foo
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: bipush        8
        10: ireturn
      LineNumberTable:
        line 6: 0
        line 7: 8

  public int foo();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String foo
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: bipush        8
        10: ireturn
      LineNumberTable:
        line 12: 0
        line 13: 8
}

The interesting thing is that the line-by-line output is identical, but the locals for bar is 1, and for foo it's 2. So it looks like space is indeed allocated for x, even though the compiler output doesn't ever use it.

UPDATE: Doing a bit more research on this, I find this page which suggests to me that, although the compiled bytecode implies that space is allocated for x, it may indeed be optimized away by the jvm. Unfortunately, I find no complete description of the optimizations performed. Particularly, the JVM documentation chapter on compiling does not mention removing unused variables from the stack. So, barring further discoveries, my answer would be that it's implementation-dependent, but it seems like the sort of optimization that any self-respecting compiler would perform. Notice too that it doesn't matter that much that this is a local variable rather than a field - in fact, local variables are the ones most likely to be optimized away, since they are the easiest to analyze and eliminate. (precisely because they are local)

Jon Kiparsky
  • 7,499
  • 2
  • 23
  • 38
  • I have no knowledge of using javap . What does stack,locals and args_size signify ? – Abhishek Singh Oct 29 '14 at 14:45
  • Like I mentioned in my other comments; it is up the the JIT to do the actual optimization. The only thing Javac does is to convert the Java source to byte-code; it doesn't do much optimization. Removing dead code is a common optimization for the JIT. – pveentjer Oct 29 '14 at 16:47
  • Yeah, this proves that, for this one case at least, an unreferenced local variable *does* take a stack slot. (It would be more instructive to add another auto variable to both methods and reference it inside the method. Then the difference in offset would be apparent.) – Hot Licks Oct 29 '14 at 20:15
  • Note that sufficient proof is present even without looking at the actual bytecodes. The `stack=2, locals=1, args_size=1` statement changes to `stack=2, locals=2, args_size=1` in the second case. The Java interpreter will allocate a stack frame based on the number of locals specified. Clearly, one more local is allocated in the second case. – Hot Licks Oct 31 '14 at 15:29
5

Expanding slightly on the testcase from @JonKiparsky:

public class StackSlotTest {
    public int bar(){
        int a;
        a = 5;
        System.out.println("foo");
        return a;
    }

    public int foo(){
        int x;
        int a;
        a = 5;
        System.out.println("foo");
        return a;
      }
}

I added the variable a to both methods, and added a set and use of it in both.

  public int bar();
    Code:
       0: iconst_5
       1: istore_1
       2: getstatic     #2                  // Field java/lang/System.out:Ljava/
io/PrintStream;
       5: ldc           #3                  // String foo
       7: invokevirtual #4                  // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      10: iload_1
      11: ireturn

Above you see that the iload_1 bytecode loads the value of a to be returned. The second stack slot is referenced. (The first is the this pointer.)

  public int foo();
    Code:
       0: iconst_5
       1: istore_2
       2: getstatic     #2                  // Field java/lang/System.out:Ljava/
io/PrintStream;
       5: ldc           #3                  // String foo
       7: invokevirtual #4                  // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      10: iload_2
      11: ireturn

In this case the value of a is loaded with iload_2, to access the third slot, because the second slot is occupied (sort of) by the (totally unused) variable x.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
3

The way a primitive variable is stored in a local variable (in bytecode) is with:

istore(for int values), dstore(for double values), fstore(for float values) and so on..

Each of them pop the top of the operator stack and write it into local variable numbered Var. So, an operator needs to push the value that need to be stored, on the top of the operator stack, just before the store operation. They works in pair.

So, if you do something like:

int i;
//...
i = 12;
//...

Your compiler will push the integer 12 in the top of the operator stack and then pop this value and write it in a local variable only at the variable initialization time.

//...
bipush 12
istore_1 1

If you never initialize your variable, the compiler cannot push a value at the top of the operator stack so the store operation is impossible. it's not a compiler optimization, it's just the way bytecode works. So, your compiler just removes your line.

Take this simple example:

public static void main(String[] args) {
    int i;
    int j = 10;
    System.out.println(j);
    i = 12;
    System.out.println(i);
}

And look when the local variable i is initialized:

public static void main(String[] p0) {
    bipush 10
    istore_2 2
    getstatic PrintStream System.out
    iload_2 2
    invokevirtual void PrintStream.println(int)
    bipush 12
    istore_1 1
    getstatic PrintStream System.out
    iload_1 1
    invokevirtual void PrintStream.println(int)
    return
}

You may notice that the local variable # 2 is used before the local variable # 1.

Take this last example:

public static void main(String[] args) {
    int i;
    int j = 10;
    System.out.println(j);
}

The bytecode would be:

public static void main(String[] p0) {
    bipush 10
    istore_2 2
    getstatic PrintStream System.out
    iload_2 2
    invokevirtual void PrintStream.println(int)
    return
}

The local variable 1 is never used and something is stored in the local variable 2. Because of that, 2 x 32 bits would be allocated here even if the local variable 1 is not used.

edit: (from Hot Licks comment)

The JVM interpreter will allocate as many local slots as are specified in the method header. They are allocated whether they are used or not. Long and double values get two slots (even though the slots are 64 bits and only one is used in most modern implementations).

A non-initialized int local variable would required 32 bits here, for example.

Pier-Alexandre Bouchard
  • 5,135
  • 5
  • 37
  • 72
  • Except that memory clearly IS allocated, as demonstrated in the other answers (and yours). In the code above slot #1 is unused. You get the above verify error because you didn't change the istore index as well. – Hot Licks Oct 31 '14 at 12:57
  • @HotLicks I didn't change the istore because I wanted to demonstrated that slot #1 is unused.. – Pier-Alexandre Bouchard Oct 31 '14 at 13:09
  • This is a proof that slot #1 is unused. No? It tought this was a good proof and I think my answer does not deserve a downvote (I will edit my answer if so) – Pier-Alexandre Bouchard Oct 31 '14 at 13:13
  • By not editing the istore you created an invalid .class file. Every load must be preceded by a store to the same location. It proves nothing. – Hot Licks Oct 31 '14 at 15:24
  • I tought. I remove the bad proof.. Can you remove the downvote? – Pier-Alexandre Bouchard Oct 31 '14 at 15:26
  • You still claim that no memory is allocated. That's clearly wrong. – Hot Licks Oct 31 '14 at 15:31
  • So, your answer is that memory is allocated to totally unused variable x? (the slot memory, in that case?) It would be a 32 bits reservation? – Pier-Alexandre Bouchard Oct 31 '14 at 15:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/64041/discussion-between-pier-alexandre-bouchard-and-hot-licks). – Pier-Alexandre Bouchard Oct 31 '14 at 15:52
  • Yes. The JVM interpreter will allocate as many local slots as are specified in the method header (`stack=2, locals=2, args_size=1` in Jon's answer). They are allocated whether they are used or not. Long and double values get two slots (even though the slots are 64 bits and only one is used in most modern implementations). – Hot Licks Oct 31 '14 at 16:22
  • @HotLicks I just edited my answer. You learn me something today. Now, it's right? – Pier-Alexandre Bouchard Oct 31 '14 at 18:00
  • The only thing you have proven here is that the in the bytecode there is space allocated for the stackframe. But there is no guarantee that the JIT doesn't shrink the stackframe. – pveentjer Nov 03 '14 at 13:07
-2

No. Reason: compile time optimization.

As you code:

public int foo(){
  int x;

  //Write code which does not use/initialize x
}

Most Java compilers will be able to figure out that x plays no role in the code execution thus they remove it entirely thanks to compilation optimization.

Even if you use:

  int x = 999;

x shouldn't be included - some dumb compilers may include it but this might change in the future.

However,

  int x = methodA();

Will definitely have x included.

Modern compilers will eliminate a lot of inefficiency in the code, so you can focus on your problems.