Where can I get more details about Julia’s Garbage Collector?
I think it uses the “Automatic Reference Counting” method but I’d like to know more details about how it has been implemented and the expected roadmap, what options are going to be added, how does it compares to other alternatives…
“Automatic Reference Counting” is an Objective-C/Swift-specific thing. Julia does not do any kind of reference counting. As @yuyichao said, it’s a non-compacting, generational, mark-and-sweep, tracing collector, which at a high level means the following…
mark-sweep / tracing:
when GC runs, it starts from a set of “roots” and walks through the object graph, “marking” objects as reachable
any object that isn’t marked is unreachable and will then be “swept away”—i.e. its memory is reclaimed—since you know it’s not reachable from the roots and is therefore garbage
non-compacting / non-moving:
there are some collection techniques that copy or move objects during the collection process
we don’t use any of these—collection does not move or copy anything, it just reclaims unreachable objects
generational:
it’s common that more recently allocated objects become garbage more quickly—this is known as the “generational hypothesis”
generational collectors have different levels of collection: young collections which run more frequently and full collections which run less frequently
if the generational hypothesis is true, this saves time since it’s a waste of time to keep checking if older objects are garbage when they’re probably not
Regarding garbage collector options:
there are none—this is good, since you can’t tune the collector wrong
If you’re having issues with garbage collection, your primary recourse is to generate less garbage:
write non-allocating code wherever possible: simple scalar code can generally avoid allocations.
use immutable objects, which can be stack allocated more easily and stored inline in other structures; as compared to mutable objects which generally need to be heap-allocated and stored indirectly by pointer, all of which causes more memory pressure.
use pre-allocated data structures and modify them instead of allocating and returning new objects, especially in loops.
Several features of Julia help with this:
Julia has immutable structures and they are widely used—struct without the mutable modifier is the default. Turns out you don’t need mutation nearly as much as you think you do. Julia encourages not making things mutable unless really necessary.
Julia allows mutation of data structures and has rich APIs for mutation. If you have allocation in a hot loop, consider if you can pre-allocate an object and pass it in and reuse it instead of allocating on every iteration.
Very interesting. To me especial intriguing is thought that I don’t need mutable structures as often as I think I need them, i.e. 90% of time.
Does something important changes in workings of Garbage Collector along the way to Julia 1.6? Compiler scheme change very much in 1.6, with great results for my user experience, so maybe GC was also changed a little bit?
No, the GC is pretty much the same. The compiler is somewhat better at seeing that some objects don’t escape the current function call, however, so they can be stack allocated (or even not allocated at all), which means they don’t become garbage in the first place. I think most of the improvement with that would have been in 1.5, which was the release in which it became possible for immutable structs containing references to mutable objects to be stack allocated rather than being forced to the heap.