Java – Does Java Copy Method Parameters to the Stack Frame?

heapjavastack

This is part of the answer about stack and heap in Java:

So, why have the stack or the heap at all? For things that leave
scope, the stack can be expensive. Consider the code:

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

The parameters are part of the stack too. In the situation that you
don't have a heap, you would be passing the full set of values on the
stack. This is fine for "foo" and small strings… but what would
happen if someone put a huge XML file in that string. Each call would
copy the entire huge string onto the stack – and that would be quite
wasteful.


This is what I read in The Java ® Virtual Machine Specification Java SE 8 Edition (Chapter 2.5.2. Java Virtual Machine Stacks):

It [Java Virtual Machine stack] holds local variables and partial
results, and plays a part in method invocation and return.


What I know is that when I pass a reference as a parameter to a method, a local variable is created inside the method. The local variable holds the same reference as the passed variable. For example, something like this

void foo(Bar bar) {
    // Do something with bar
}

turns into something like this:

void foo(Bar bar) {
    Bar localBar = bar;
    // Do something with localBar
}

So my question is:

Does Java copy method parameters to the stack frame of the called method? From the answer I refer to at the start of page, I understand they are copied, and it's a deep copy. Almost sure, I am wrong.

Best Answer

In Java, the distinction stack vs. heap is not very meaningful, because Java doesn't give you a choice where values live. Conceptually, all objects live somewhere on the heap, but in Java an object is not the same thing as a variable. Local variables and method parameters use space on the stack, but variables never hold an object directly. The variables either contain primitive values like int, or references to objects.

In Java, variable assignment always performs a shallow copy: for primitive values, the value is copied. For objects, the reference is copied. The object that is referenced is not copied: the original reference and the copied reference both refer to the same object.

Method calls work very much like variable assignment to the parameters. When a method is called, you can think that a stack frame with the method parameters is allocated on the stack, and that we then assign the argument values to each parameter variable.

Because a shallow copy of all argument values is performed, you cannot change outside variables by assigning to a parameter within a method:

void outer() {
  int i = 4;
  inner(i);
  // i will always be 4 at this point
}

void inner(int j) {
  j = 7;  // this assignment only affects the *local* j
}

But if you pass an object there is no deep copy, so changes to the object are visible outside of the method:

void outer() {
  ArrayList<String> xs = new ArrayList<>();
  inner(xs);
  // xs has been modified at this point, and contains "foo"
}

void inner(ArrayList<String> ys) {
  ys.add("foo");
}

The part from the answer that you quoted does not explain how Java works, but why it is desirable to not do deep copies for each method call.

The behaviour of Java's method calls is very similar to C and C++: everything is passed by value. The difference is that Java objects are not an object in the C and C++ sense, but behave like a pointer to the actual object. For variable assignment and method calls, that pointer is then passed by value.

Related Topic