Arenas
Arenas are a way that Java provides developers to allocate memory in a way that is particularly useful for creating bindings. Arenas are like a stack of memory, and its space can be split in various ways, and its lifetime can be set by various types. The main idea of where arenas can be used is that they can create space to store objects in Java called Memory Segments. These memory segments can store data such as variables, data structures, and functions in a space that the garbage collector treats differently. That means information stored in these arenas can be passed to and from foreign functions without worrying about whether Java’s garbage collector has tampered with the space.
There are four different types of arenas: confined, automatic, shared,
and custom. Confined arenas and shared arenas are very similar. They both will live as
long as the Java program unless they are manually closed by the user using
the .close()
method on the arena object. The key difference between the
two is that confined arenas can only be accessed by a single thread, while
shared arenas can be accessed by multiple threads. This causes a weird
interaction with shared arenas. When a confined arena is closed, its memory
is immediately freed and that’s all there is to it. When a shared arena is
closed, it invalidates all Java references to the space in memory, but it does
not immediately free it as the process takes longer, meaning that the space
in memory is technically alive for a very short amount of time after the
arena is closed. These arenas are useful for creating Rust bindings because they can
guarantee a space in memory cannot be accessed once closed, so they can be
implemented into functions to guarantee proper memory safety practices.
The API descriptions for automatic arenas typically vaguely describe their closing behavior, such as “the garbage collector eventually frees it automatically”. To better describe its behavior, The garbage collector will only free the automatic arena either at the end of the Java program or when it determines that the arena is unreachable. But what does the garbage collector see as unreachable?
Testing will show that Java will not close the arena even if every memory segment inside is set to null. The information inside the arena has no bearing on the garbage collector’s decision to keep it around. However, a way to guarantee that the garbage collector determines the arena as unreachable is to set the arena to null. This means that automatic arenas can be useful and reliable for creating bindings as well, especially if it is not clear when a certain arena should be closed. The only downside of the automatic arena is its interaction with the garbage collector. It is possible this could cause some sort of increased overhead.
With an Arena, you can call arena.allocate(
size, alignment
)
to allocate
memory within the arena. Allocations cannot be individually freed with
Arenas, it’s either all or nothing. Global Arenas
are useful for set-and-forget things, like for loading the Rust library, since this
does not need to be freed. Confined Arenas are good for data that cannot be
safely shared across threads, so for types that don’t implement the
Send trait. Auto Arenas are nice if it is difficult to figure out
when something should be deallocated. Although this isn’t very common as drop()
should be called on Rust objects that require cleanup, and Java’s
garbage collector will not take care of this.
For more information on arenas, visit Oracle's official documentation.