I read a really, really interesting article on memory management strategies for the Erlang VM. It was written as a thesis by Jesper Wilhelmsson I thought it might be nice to discuss the differences between Erlang’s memory setup and Oracle’s Java VM.
As a real short introduction for those who have never heard of Erlang; it is a functional language that uses asynchronous message passing as its basis for concurrency. The message passing uses copy semantics, making distribution over more than one Erlang VM, running on more than one machine essentially transparent to the programmer.
Erlang and Java are similar in the sense that both use a virtual machine to abstract the hardware into a portable layer. Both languages employ machine independent byte code. Both run-time systems rely on garbage collection to free the programmer of doing memory management.
Threading overhead in Erlang is very low. I believe that the memory requirements for a thread in Erlang is about 512 bytes. In Java threads typically need about 512 kilobytes, about 1000 times more. For a programmer, the upshot is that creating threads to do some work asynchronously is not something you have to sit down and think about. Typical Erlang systems have thousands or tens of thousands of threads. There is no futzing with threadpools and executors like we do in Java.
From what little I have dabbled with it, I found Erlang to be a pleasant compromise between a functional language, and a language that allows you to write real-world applications. (I know I’ll get flak for this) Robust distributed error handling is a pleasant surprise and writing a network server of any kind is actually easy. The state-machine approach to web servers makes rolling back on errors completely natural.
But this post is not about the programming model of Erlang. It is about the way the Erlang VM deals with memory.
The current Java virtual machine uses what an Erlang programmer would
call a shared heap topology. There is one big heap that is used by all threads. Most memory is allocated on that heap. In addition to the heap, the JVM uses some specialised data areas like the code cache and the permanent generation. These too are shared between all threads.
By contrast, Erlang uses a private heap topology. Each thread has its own tiny heap that contains all data the thread uses and the thread’s stack as well. All data for a thread is on that local heap. It is reserved when the thread is created. When the thread dies, the entire heap is simply returned to the pool of free memory.
Aside from the private heaps, all threads share access to a so-called binary heap and a message heap. These are specialised heaps. The binary heap is for allocating large chunks of arbitrary data that are likely to be shared between threads. This is where for example file input or network buffers live.
The message heap is a heap for data that is used in messages. Messages too are shared between processes. Messages are passed between threads by copying a pointer over from the sending thread to the receiving thread. The data for the message is stored on the message heap.
I was impressed by the Erlang memory model. It just strikes me as a lot more scalable than Java’s single heap model. The language semantics and the memory model match beautifully.
For instance; the simple fact that heaps are private to a thread relieves the threads of all forms of lock checking on their own data.