Garbage collector and memory management

I have a data type that holds pointers to C data (for passing to a library):

mutable struct Data

When a Data object is released, I want to perform some operations on the pointers to free the corresponding memory (i.e. pass them to some function in my C library). The first thought would be to attach a finalizer function in the constructor such as this:

Data(...)  = begin
  self = ... # perform initialization and then
  finalizer(kill, self)

kill(x::Data) = begin
  # release C pointers here

However, in my case some of the C pointers might be referenced by multiple Data objects. This means that I cannot simply free them each time a Data object becomes inaccessible Julia-side.

I can imagine one and a half way to do this:

  • the first way would be to manually implement reference-counting for the C pointers (increment refcount in the constructor, decrement it in the finalizer);
  • the second one would be to somehow attach the pointers to Julia’s tracing GC.

The second way feels more elegant/efficient, but I’m not quite sure how to do this. Maybe I could wrap a trivial Julia type around the C pointer, make my Data struct contain pointers to these wrappers (keeping these alive as long as the Data is), and then do the pointer freeing at the wrapper level? How then can I be sure that, if a certain pointer is referenced more than once, all the wrappers will be the same Julia object (and thus the finalizer will only be invoked when the last one of them dies)? Should I make the wrappers Val{my_pointer}() values?


Not really.

This is C-interop so you should do it the same way you would do in C.

Any knowledge of a general implementation of the first alternative, i.e. reference counting? E.g. reference counted resources, arrays, C-allocated objects etc.

See also Resource pooling - to avoid dynamic allocation