“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
- 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.