Expand description
Julia memory management.
This module contains all structs and traits that are used to deal with memory management and enforcing a degree of compile-time memory safety by applying reasonable lifetime bounds to Julia data.
The Julia GC is unaware of any references to Julia data existing outside of Julia itself. To make these references known to the GC, data that is used must be rooted. This ensures the GC doesn’t accidentally identify the data as unused and free it.
A scope must be created before data can be rooted, the sync runtime lets you create a scope
directly with Julia::scope
. This method takes a closure which takes two arguments, the
second is a GcFrame
. A GcFrame
is used to store roots, each scope has its own GcFrame
,
any Julia data rooted in this frame is guaranteed to be protected from being freed by the GC
until you leave the scope.
There are two other frame types, AsyncGcFrame
and NullFrame
, the first is used by the
async runtime and is used with several async functions, while the latter is only available
when calling Rust from Julia with its ccall
interface. All these frame types implement the
Frame
trait.
It’s important to avoid rooting data longer than necessary. As more data is rooted, more
memory remains in use, which causes the GC to run more often and require more time to run.
Scopes can be nested by calling Frame::scope
, they form a single nested hierarchy. Any
data rooted in the frame provided to this new scope is rooted until that new scope ends.
Lifetimes ensure no data that is rooted in the child scope can be returned to the parent
scope.
To return rooted data from a child scope, it must be rooted in an ancestral scope. The method
Frame::output
can be used to reserve an Output
in a frame. Methods that return rooted
data generally take an implementation of PartialScope
or Scope
. Methods that take a
PartialScope
only need to root a single value, both Output
and mutable references to an
implementation of Frame
implement this trait. In the first case the data is rooted in the
frame targeted by the output, in the second it’s rooted in the current frame. Methods that
take a Scope
need to root temporary data. While mutable references to an implementation of
Frame
implement this trait, Output
doesn’t. An Output
must first be upgraded to an
OutputScope
by calling Output::into_scope
.
Not all data needs to be rooted, roots form the starting point for the GC to trace the entire
graph of reachable data. As long as data is reachable from a root, it won’t be freed. Julia
modules provide global scopes, their contents are rooted unless the module is reloaded or the
data is overwritten some other way (e.g. by mutating a global value). If you never use the
result of a Julia function call the result doesn’t need to be rooted either. Most methods that
return rooted Julia data have a corresponding method that leaves the result unrooted. These
methods often only require a Global
, which ensures that this data can’t be accessed before
Julia has been initialized and that reasonable lifetime bounds are applied.
The final tool that is available to manage the rootedness of Julia data is the
ReusableSlot
, it can be created with Frame::reusable_slot
and provides a slot in that
frame that can be overwritten.
Modules
Frames store GC roots.
Manage the garbage collector.
Access token for global Julia data.
Modes handle memory management differences between the sync and async runtime.
Outputs to root data in a parent scope.
A reusable slot in a frame.
Root Julia data in a specific scope’s frame