How to Obtain Efficient Garbage Collection of C-Ptrs

I am working with a C++ wrapper where the most common type is a wrapper around a shared pointer to memory. Essentially an array view. Lets call this type MyArray.

My general question is given function which returns MyArray, how will Julia’s GC operate on that object given that it knows nothing about the underlying heap allocation and when that will be freed. From what I understand Julia sees MyArray as taking only 8 bytes and the memory that pointer refers to in C belongs to another heap which Julia has no information about.

Julia’s GC seems like it would be incapable of understanding when GC needs to be run on an object like this since the heuristics are based heap allocations and from Julia’s POV the heap has very few allocations. We have run into such issues when generating lots of these array views inside of a loop which are not being GC’ed fast enough since Julia does not know they actually use a lot of RAM. (We can actually OOM). We can add GC.gc to each loop iteration but that is incredibly slow.

Is there some way around this or to give Julia extra information about the underlying allocation? Thanks!

I recently fixed a similar issue in CodecZlib.jl by getting the C library to use jl_malloc and jl_free instead of malloc and free Ref: C struct garbage collection not run frequently enough - #14 by vchuravy

To clarify, we are using c++ class constructors and destructors for memory managment. Forcing the garbage collection calls with GC.gc calls our deconstructor.

The jl_malloc and jl_free is interesting. We are not necessarily able to modify the source code of the original library so I’m not sure this would work. Maybe I’m missing something about how you implemented this in Zlib though.

These classes are logical representations of data. They can be placed on different target devices and have no explicit notion of using “malloc”. By calling the deconstructor, it notifies the runtime that the physical data can be free’d.

Yes jl_malloc and jl_free with finalizers only work in this case because zlib has support for custom allocators, only uses CPU memory, and is a thread-safe library without its own runtime.

Using the Julia GC to clean up resources in general cases can get very tricky, so it might be better to just require C-style manual calls to the clean-up functions, with some functions/macros for common patterns.