Immutable and mutable objects, references and the heap

Immutable and mutable objects, references and the heap

An object whose state cannot change after it has been constructed is called immutable (unchangable). The methods of an immutable object do not modify the state of the object. In Kotlin, all number types, strings, and tuples are immutable. The classes Point, Date, Student, and Card we defined above are all immutable.

Indeed, if we try to change the coordinates of a Point, we get an error:

>>> p.x = 7
java.lang.IllegalAccessError: tried to access field ...

In other words, once a Point object has been created, its fields cannot be modified.

It is possible to define a mutable case class: we need to put the var keyword in front of the field names:

>>> data class MPoint(var x: Int, var y: Int)
>>> val p = MPoint(3, 5)
>>> p
MPoint(x=3, y=5)
>>> p.x = 7
>>> p
MPoint(x=7, y=5)

Note that we could change the $$x$$-coordinate of the point p even though we defined p as a val-variable. Remember that this only means that the name p will always refer to the same object. It is possible to change the fields inside this object.

Mutable objects can lead to tricky mistakes. Consider the following code:

>>> val p = MPoint(3, 5)
>>> val q = p
>>> q.x = 7
>>> q
MPoint(x=7, y=5)

What is the value of p at this point? Surprisingly, p has changed as well:

>>> p
MPoint(x=7, y=5)


MutableList objects are of course mutable, and so the same effect can appear for them:

>>> val a = mutableListOf(1, 2, 3, 4)
>>> val b = a
>>> a[2] = 99
>>> b
[1, 2, 99, 4]

(Note again that even though we have defined a as a val variable, it is possible to change the contents of a.)

References and the heap

Why does this happen? To understand this, we need to understand how variables store objects.

All objects are stored in an area of the runtime system called the heap. Objects cannot exist anywhere else.

A variable is just a name for an object on the heap. You can think about a variable as a reference to the object on the heap. The reference uniquely indicates the object on the heap. (If you learnt C, you can think about this reference as a pointer. In reality it may not really be a memory address.)

An assignment operation (as in val q = p or val b = a above) creates a new name for an object on the heap. p and q are in fact two different names for the same MPoint object, and a and b are two names for the same MutableList object:

This problem can never happen for immutable objects, and so it is preferable to use immutable objects whenever that is possible.

Local variables

Now that we know that all objects live in the heap, you may wonder where the variable names, that is, the references, are stored.

A reference that is a field of an object (or an element in a list), is stored inside that object in the heap.

Most other references are local variables of some function or method. They are stored inside a piece of memory called the activation record or stack frame of the function. The activation record is created automatically each time the function is called. For instance, this function

fun test(m: Int) {
val k = m + 27
val s = "Hello World"
val a = listOf( s.length, k, m )
}

has four local variables, namely m, k, s, and a. (The parameters of a method are local variables, with the only difference that the runtime system automatically copies the argument value into the variable when the method is called.)

The following shows the activation record of test and the heap when test(13) is called, just before the function returns:

Garbage collection

Kotlin objects are garbage collected: If the runtime system runs out of memory, it will check all the objects on the heap. If an object no longer has any reference pointing to it, the object is no longer useful, and will be deleted. It is hard to predict when garbage-collection will happen. If you run a small program only, probably no garbage-collection at all occurs.

Garbage collection allows the programmer not to worry about the memory management. There are other languages which do not provide an automatic garbage collection. For example, in C++ the programmer is responsible for the memory management. It is common for C or C++ programs to contain mistakes where objects are created but never destroyed, and so more and more unused and unusable objects fill up the heap. Such a program is said to contain a memory leak.

 Immutable and mutable objects, references and the heap