Java: Call by reference, or call by value?

My attention was drawn to a trainwreck of a discussion on Reddit, which was triggered by another trainwreck of a discussion on an enterprise Java community web site.

The vexed question: Is Java pass by value, or pass by reference?

My short answer, carefully phrased: Java passes both object references and primitive types by value.

I’ll now unpack that answer and explain it, and talk about why there’s so much confusion. To do so, I need to go back to the very early days of programming…

Once general purpose computers were built, it was quickly discovered that it would be really useful if you could call a chunk of code multiple times, from multiple places.

For instance, most early CPUs (including popular 8 bit CPUs like the Z80 and 6502) didn’t have a general purpose multiply instructions. It was very useful to be able to write a piece of code that would multiply two numbers together and return the result. Because you’d want to multiply numbers a lot from different parts of your program, there needed to be a standard way for the code to know where to jump to when it was done computing the answer for you.

In the early days, this was done in one of two ways. One was to have the caller put a return address into a register. When the multiply routine was done, it would jump to whatever address was in that register, resuming the main program, which could then use the result of the multiplication.

The PDP-8 used a slightly different approach: the instruction to call a subroutine would store the return address in a reserved piece of memory immediately before the subroutine. The subroutine code would be called, would find the return address immediately before it in memory, and could therefore jump back there.

The problem, of course, is that soon programmers wanted their subroutines to be able to call other subroutines, or even call themselves. Both of these methods of passing return addresses failed to allow that. The PDP-8 approach was also ugly because it meant you couldn’t store subroutines in ROM.

So by the time of the PDP-11 and 6800, the call stack had been invented. With a call stack, when you called your multiply subroutine the address of the next instruction after the call was placed on the call stack automatically by the CALL instruction. The processor then jumped to the subroutine code. The code did its multiplication and put the result in a register as before, then pulled the address off the top of the stack and called it — hence jumping the CPU back to the point immediately after the CALL instruction. Popular 8-bit home computer CPUs including the 6502 and Z80 had hardware call stack support.

This was a big improvement, but still not ideal. The problem was, the subroutines and the code that called them had to agree on which registers to use for which values before the call, and which registers to find the results in after the call.

So later CPUs, like the 68000, added instructions to make it easy to push additional values onto the stack — not just return addresses. Now to call your multiply subroutine you’d push the two values onto the stack and CALL the subroutine. The subroutine would pull two values from the stack, multiply them, and push the result onto the stack before returning. Your code would then pull the result from the stack and use it. The combination of return address and subroutine parameters was called a stack frame. This call model was the one used on the PDP-11, and hence became the call model for the C programming language.

Now, stack-based parameters work well, but there’s a potential problem once you start dealing with values that can occupy a large amount of memory — such as strings. How do you pass a string to a subroutine?

This problem had been tackled before the invention of call stacks, of course. The general answer was that rather than copying the data around everywhere, you would just pass around a pointer to the start of the string. Not only is it a lot more efficient, you also don’t use up as much stack space — and in early computers, stack space was a significant concern.

So in most languages, when you passed an integer and a string to a function, what actually happened was that the value of the integer was placed on the stack; but a pointer to the string was placed on the stack, rather than the value of all the characters in the string itself. These two approaches were named ‘call by value’ and ‘call by reference’, respectively. (For example, see “Semantic Models of Parameter Passing”, Richard E. Fairley, 1973.)

When object-oriented programming was invented, the exact same issue applied to objects. The value of an entire object, with all its methods and fields, could be very large. Rather than placing the actual value of the object on the stack, instead a reference to the object was placed on the stack.

For now, though, let’s stick with strings. When you pass a string to a C function, the function may be able to change the text of the string, but it can’t change the fact that your string variable will still be looking for the string at the same memory address once the subroutine returns.

So at the language level, function parameters are conceptually passed by value, even though those values may actually be the addresses of an object or string. Hence K&R states:

One aspect of C functions may be unfamiliar to programmers who are used to some other languages, particularly Fortran. In C, all function arguments are passed “by value”. This means that the called function is given the values of its arguments in temporary variables rather than the originals.

This description is clear for C, because C always makes pointers visible. Your string is manipulated using a char * pointer variable. It’s handed to the subroutine as a char *. It’s clear that the subroutine gets a copy of that pointer, not access to your pointer. Whatever the subroutine does, the caller’s pointer variable will still point to the same address when the subroutine is done.

Java handles parameter passing the same way in the JVM. Primitive values such as ints have their values placed on the stack; objects have the a copy of the address of the object placed on the stack. The called code can mess with the contents of the object, but when it returns, your variable will still point at the same object. That is, strictly speaking in Java the value of a variable is which object it refers to, not the value of the object.

This is, of course, a highly unusual definition of “value”.

James Gosling goes to the bank and finds all the money has been emptied from his account. When he complains, the teller says “But your account has the same value it had yesterday — your account number still references the exact same account!”

Another reason why people get confused about Java’s call semantics is that you never actually see the addresses of objects. Unlike in C, where you can mess with pointers (and indeed have to), Java hides that detail of the machine-level implementation. You’re simply not allowed to access the JVM’s pointers in the high level Java language. Variable names which are bound to the address of an object are automatically and invisibly dereferenced to the referenced object when necessary.

As I put it in my earlier article about final parameters, “The Java Thing a is really like C++ Thing *a, and Java’s a.method() is really C++’s a->method(), not a.method().”

So in Java, you don’t need to explicitly dereference your object variable in order to call one of its methods; the compiler does it for you.

So when you pass a bunch of variables to a method, Java passes both the variables which were assigned object values and the variables which were assigned primitive values by value; it makes copies of all of them to give to the method code. It’s just that the variables which were assigned object values actually contain references to the object data, which will be automatically dereferenced when necessary. Most of the time this is very convenient, and it makes the language a lot safer by prohibiting various kinds of misbehavior with pointers.

So in Java,

int x = 3;
String y = "three";

are two totally different operations. They only look like they’re the same concept because of some syntactic sleight-of-hand performed by the compiler.

If someone asks you what the value of y is after the above code runs, you might say that it’s the string “three”. In fact, though, the true value of y is a reference to the string “three”. You just never see that true value.

In C, on the other hand, the difference is explicit:

int x = 3;
char *y = "three";

The asterisk is the clue that two different sorts of things are being done here.

This distinction also comes up when considering the == operator. Java’s == compares the true underlying value of variables; x == y only if x and y are both references to the exact same object. To see if x and y have the same object value when dereferenced, you need to use a method such as equals() or compareTo(). Worse still, the default implementation of equals() is ==, unless the class overrides it.

So what would call-by-reference look like? Well, in C we can do this:

int x = 3;
f(&x);

This calls the function f, passing it a reference (the address of) the value in the variable x. The function is thus able to change the value in x, as it is then seen in the calling code.

This brings up a second reason why the discussion on Reddit is a trainwreck. People say that a language is call-by-value when a subroutine cannot change the value of a variable passed as a parameter. Unfortunately, because Java invisibly dereferences your object pointer variables, that’s not really true:

static void f(StringBuffer o) {
  o.delete(0, o.length());
  o.append("This is impossible.");
}

public static void main(String[] args) {
  StringBuffer x = new StringBuffer("Java is call by value.");
  f(x);
  System.out.println(x);
}

In the above code, f() clearly changes what we’d colloquially call the “value of x”, as we see it at Java language level. However, it does so without changing which object x points to. To rephase in Java language specification terms, before the method call x is a reference to a string object which has the value “Java is call by value.” After the method call, x is still the same reference to the same string object, which now has the value “This is impossible.” But again, you never see the references which are being compared, so the situation is confusing.

Or as I wrote in Java annoyance: final parameters:

It seems that the designers of Java wanted to make sure nobody confused their object pointers with the evil manipulable pointers of C and C++, so they decided to call them references instead. So Java books usually say that objects are stored as references, leading people to think that objects are passed by reference. They’re not, they’re passed by value, it’s just that the value [on the stack at JVM level] is a pointer.

If you’re still confused at this point… well, I can’t say I blame you. We should probably both blame James Gosling.