Erlang Garbage Collector
erlang-solutions.com
erlang-solutions.com
This is one of the cool things about the Erlang VM. Each process (lightweight thread) has its own heap! In other VMs threads can affect each other by generating garbage and causing GC pauses. In Erlang even the heaps are isolated.
Concurrent compacting collectors are pretty tough however, and a single process STW should be much faster than a global one, since it will be (at worst) proportional in time to the per process heap size.
I haven't looked at this for a while but I believe it's still an issue.
https://github.com/cockroachdb/cockroach/issues/2007 https://github.com/golang/go/issues/18896 https://github.com/golang/go/issues/14045
"Semispace" is one term to refer to the family of garbage collectors that Erlang's derives from. The heap is divided into two spaces, hence "semispace"
So Go's GC is neither. Go's gc is tuned for lower latencies, and that tends to rule out moving collectors (there are ways of having incremental moving collectors, but they complicate the runtime a bit).
There have been implementations of Erlang with shared heap in the past, before BEAM became heavily multi-threaded: https://www.researchgate.net/publication/2371933_A_Case_for_...
I think it would be nice to see this tried again. Perhaps the issue is scaling to future systems with potentially hundreds of cores?
Also remember malloc isn't free either it takes time to execute.
GC on a small heap is fast; especially since the language does not allow for circular references in the heap. Almost always, if you're you've got a large heap in your Erlang process, you're doing it wrong; large data generally belongs somewhere else like ETS, the in memory term storage.
If only it were typed, ah... (no, dialyzer doesn't count).
On the other hand, I had some bit twiddling code that was 13x slower than C. I know because I rewrote that section into C as a NIF and benchmarked. The nice thing then was my overall 95% Elixir app became about as fast as a 100% C app.
Which is not so important, as their strength is eliminating IO waits and context switches which are usually much costlier.
Their GC is top notch.
In erlang you just execute some erlang commands and in most cases you don't even drop connections, it's like changing your cylinders in engine when it's still running. Of course if your code has errors it can go wrong, but typically you are only spammed with some errors from this one service until you fix the error and reload code.
After you do some code upgrades in erlang, you miss this functionality in all other languages.
There's been some proposals for Multi-party session types, but none that are practical by any means.
The process model also makes it very tempting to build a monolithic-microservices and build an erlang VM cluster which can add complexity if you already have an infrastructure like kubernetes or works with other languages.
FWIW, these things I just mentioned can also be considered as Erlang/Elixir's strengths depending on your requirements.
BEAM works best if you can model your work as a process, optionally with state, getting messages and sending messages. If that doesn't fit your program, it's not going to be a good fit.
There's not a long of bindings into other systems; GUI bindings are problematic -- OTP ships with wxWidgets, but it doesn't feel like Erlang programming to use; I didn't evaluate gtknode, but last commit 2 years ago seems iffy for a project addressing gtk (which I was under the impression changes frequently). If you're dealing with networky things, and the protocol is well documented, it's easy to create your own bindings; it's possible to create your own bindings to other things, but dealing with Erlang data types in C is some amount of effort.
Hotloading will warp your mind. For a side project I had written in C, I wanted to hotload the main loop so I didn't need to lose state when I restarted (serializing the state was out of the question because I'm keeping state in a library that doesn't offer serialization); I looked into writing bindings for the interfaces I needed, but it was going to take too much work, so I ended up refactoring my code so I could use dlopen to hot load the main loop in C. :D
There are some issues I've run into with OTP, but those aren't really limitations of the BEAM. Lots of OTP doesn't impose explicit limits (or have ways to impose limits), but instead has scaling issues when you hit large numbers / exciting conditions. Most of these _are_ easy to fix, but some of them are frustrating, and are clearly only still broken because there's not a huge community of people, some of which would run into things. For example, the virtual binary heap that was mentioned in the article -- this is a great solution to the problem (the problem is: if you have a simple process that just passes around binaries, it doesn't generate enough garbage on its heap to trigger GC, so the shared binary heap grows rather large), but the fix is pretty recent because not many people ran into it, and when they ran into it, they tended to fix it through manual GC rather than fixing the GC.