How to turn off garbage collection (GC) for Elixir processes
Before going into detail. You might want to learn more about how GC work in Elixir/Erlang. This blog post explain very well the high level architecture of that, I highly suggest you read Erlang Garbage Collection Details and Why It Matters
Ok, now you might probably ask why someone would even want to turn off garbage collection. In general, GC is an expensive process. If your system do it too often then it will consume lots of CPU power. GC in Erlang/Elixir is per-process based, mean that each process will have it own garbage collector.
“Real-time applications rely on long-lived processes in order to have a direct connection to clients - this allow them to send data immediately when the data is available. There’re a downside to this though. It’s possible that processes get stuck in a state where garbage collection doesn’t occur, but memory isn’t being used. This can cause large memory bloat when amplified across thousands of processes.” If that is the case, you might want to turn off GC for those processes.
Luckily, Erlang have this option for us whenever we spawn a new process. You can have a look into the detail here http://erlang.org/doc/man/erlang.html#spawn_opt-2. All we need to do is to set the message_queue_data to either off_heap or on_heap.
off_heap: All messages in the message queue will be stored outside of the process heap. This implies that no messages in the message queue will be part of a garbage collection of the process.
on_heap: All messages in the message queue will eventually be placed on heap. They can however temporarily be stored off heap. This is how messages always have been stored up until ERTS 8.0.
In Elixir, whenever you spawn a new process, there is an option that is not obvious to everyone which is spawn_opt
defmodule ApiWorker do
use GenServer
def start_link() do
GenServer.start_link(__MODULE__, %{},
name: :"ApiWorker",
spawn_opt: [message_queue_data: :off_heap]
)
end
end
To verify the process is actually off heap. You can try the following
# Go to iex
# Get a sample process id
pid = Process.list |> Enum.at(10)
:erlang.process_info(pid, :message_queue_data)
# => {:message_queue_data, :on_heap}
{:ok, p} = ApiWorker.start_link()
:erlang.process_info(p, :message_queue_data)
# => {:message_queue_data, :off_heap}
That’s it.