Forcing GC to garbage collect a map key-value

Imagine the following code:

handle_cast(compute, State) ->
  {noreply, maps:put(key, compute(), State)};

This handle_cast/2 may be called multiple times, always updating the same key. The issue is that compute() uses a lot of memory (to be more precise, it points to data allocated on the GPU) and therefore we want to Garbage Collect whatever is on key before we call compute() again. In other words, we want to do this:

handle_cast(compute, State) ->
  FreshState = maps:remove(key, State),
  erlang:garbage_collect(),
  {noreply, maps:put(key, compute(), FreshState)};

The question: is the code above enough? If not, is there something we could do to force those particular entries to be GCed?

Thank you!

3 Likes

To make sure the old map State with value is no longer live you might want to do

handle_cast(compute, State) ->
  FreshState = maps:remove(key, State),
  tail_call(FreshState).

tail_call(FreshState) ->
  erlang:garbage_collect(),
  {noreply, maps:put(key, compute(), FreshState)};
2 Likes

@sverker could you explain the difference between your solution and what @josevalim suggested?
They look equivalent to me but you’re the expert.

1 Like

My guess is the following:

  1. The data in a function is stored in registers
  2. However, if we remove a key from the map and no longer use its previous variable, a register may still point to it
  3. Unused registers may be cleared in the middle of a function depending on the compiler but unused registers are always cleared before a tail call
  4. Therefore adding a tail call will force registers to be cleared and the old map to no longer be referenced, which would allow its contents to be GCed
3 Likes

Almost sounds like you want to hibernate between handle_cast:s.

2 Likes

It used to be necessary to do tricks like that to ensure that garbage was removed, especially in JAM (Joe’s Abstract Machine) but also to a lesser degree in early versions of BEAM.

Modern BEAM is very careful to never keep a reference to a term that will not be used again. Therefore, in @josevalim’s original code, no reference to the original map is kept when erlang:garbage_collect/0 is called.

10 Likes