3. Arenas¶
An arena is an object that encapsulates the state of the Memory Pool
System, and tells it where to get the memory it manages. You typically
start a session with the MPS by creating an arena with
mps_arena_create()
and end the session by destroying it with
mps_arena_destroy()
. The only function you might need to call
before making an arena is mps_telemetry_control()
.
Before destroying an arena, you must first destroy all objects and
data in it, as usual for abstract data types in the MPS. If you can’t
destroy the arena properly (for example, because your program has
crashed and you are at the debugger prompt), you can still call
mps_telemetry_flush()
explicitly.
Other types of objects in the MPS are created “in the arena”. They are part of the world within the arena, and may interact and affect each other.
Note
The MPS allows creation of multiple arenas, but you would only do this in unusual circumstances. It might be useful to have two active arenas and to try different things out in them, or you might be in the process of integrating two pieces of software that each independently uses the MPS.
Arenas do not normally interact, but they compete with each other for resources, and references from one arena to another are not traced, though you can declare roots pointing from one arena to another. It is not efficient to have multiple arenas containing automatically managed pools: if you find yourself in this situation it’s best to find a way to move all the automatically managed pools to one arena.
The open source MPS comes with two classes of arena, Client arenas and Virtual memory arenas. These differ in the way that they acquire the memory to be managed.
Note
The MPS is designed to be extensible with new arena classes. If you need features that are not provided by any of the open source arena classes, contact us.
-
mps_arena_t
¶ The type of arenas.
An arena is responsible for requesting memory (3) from the operating system, making it available to pools, and for garbage collection.
-
mps_arena_class_t
¶ The type of arena classes.
-
mps_res_t
mps_arena_create
(mps_arena_t *arena_o, mps_arena_class_t arena_class, ...)¶ Create an arena.
arena_o
points to a location that will hold a pointer to the new arena.arena_class
is the arena class.Some arena classes require additional arguments to be passed to
mps_arena_create()
. See the documentation for the arena class.Returns
MPS_RES_OK
if the arena is created successfully, or another result code otherwise.The arena persists until it is destroyed by calling
mps_arena_destroy()
.Note
There’s an alternative function
mps_arena_create_v()
that takes its extra arguments using the standard Cva_list
mechanism.
-
mps_res_t
mps_arena_create_v
(mps_arena_t *arena_o, mps_arena_class_t arena_class, va_list args)¶ An alternative to
mps_arena_create()
that takes its extra arguments using the standard Cva_list
mechanism.
-
void
mps_arena_destroy
(mps_arena_t arena)¶ Destroy an arena.
arena
is the arena to destroy.This function checks the consistency of the arena, flushes the telemetry stream and destroys the arena’s internal control structures. Additionally, virtual memory arenas return their reserved address space to the operating system if possible.
It is an error to destroy an arena without first destroying all generation chains, object formats, pools and roots created in the arena, and deregistering all threads registered with the arena.
3.1. Client arenas¶
#include "mpsacl.h"
-
mps_arena_class_t
mps_arena_class_cl
(void)¶ Return the arena class for a client arena.
A client arena gets its managed memory from the client program. This memory chunk is passed when the arena is created.
When creating a client arena,
mps_arena_create()
takes two extra arguments:mps_res_t mps_arena_create(mps_arena_t *arena_o, mps_arena_class_t mps_arena_class_cl, size_t size, mps_addr_t base)
base
is the address of the chunk of memory that will be managed by the arena.size
is its size.If the chunk is too small to hold the internal arena structures,
mps_arena_create()
returnsMPS_RES_MEMORY
. In this case, you need to use a (much) larger chunk.Note
You don’t have to provide all the memory up front: you can call
mps_arena_extend()
later on.Client arenas have no mechanism for returning unused memory.
-
mps_res_t
mps_arena_extend
(mps_arena_t arena, mps_addr_t base, size_t size)¶ Extend a client arena with another block of memory.
base
is the address of the block of memory that will be managed by the arena.size
is its size.Return
MPS_RES_OK
if successful, or another result code if it fails.
3.2. Virtual memory arenas¶
#include "mpsavm.h"
-
mps_arena_class_t
mps_arena_class_vm
(void)¶ Return the arena class for a virtual memory arena.
A virtual memory arena uses the operating system’s virtual memory interface to allocate memory. The chief consequence of this is that the arena can manage many more virtual addresses than it needs to commit memory to. This gives it flexibility as to where to place blocks, which reduces fragmentation and helps make garbage collection more efficient.
When creating a virtual memory arena,
mps_arena_create()
takes one extra argument:mps_res_t mps_arena_create(mps_arena_t *arena_o, mps_arena_class_t arena_class_vm(), size_t size)
size
is the initial amount of virtual address space, in bytes (1), that the arena will reserve (this space is initially reserved so that the arena can subsequently use it without interference from other parts of the program, but most of it is not committed, so it doesn’t require any RAM or backing store). The arena may allocate more virtual address space beyond this initial reservation as and when it deems it necessary. The MPS is most efficient if you reserve an address space that is several times larger than your peak memory usage.Note
The MPS asks for more address space if it runs out, but the more times it has to extend its address space, the less efficient garbage collection will become.
If the MPS fails to reserve adequate address space to place the arena in,
mps_arena_create()
returnsMPS_RES_RESOURCE
. Possibly this means that other parts of the program are reserving too much virtual memory.If the MPS fails to allocate memory for the internal arena structures,
mps_arena_create()
returnsMPS_RES_MEMORY
. Eithersize
was far too small or the operating system refused to provide enough memory.
3.3. Arena properties¶
-
mps_word_t
mps_collections
(mps_arena_t arena)¶ Return the number of flips that have taken place in an arena since it was created.
arena
is the arena.
-
size_t
mps_arena_commit_limit
(mps_arena_t arena)¶ Return the current commit limit for an arena.
arena
is the arena to return the commit limit for.Returns the commit limit in bytes (1). The commit limit controls how much memory the MPS can obtain from the operating system, and can be changed by calling
mps_arena_commit_limit_set()
.
-
mps_res_t
mps_arena_commit_limit_set
(mps_arena_t arena, size_t limit)¶ Change the commit limit for an arena.
arena
is the arena to change the commit limit for.limit
is the new commit limit in bytes (1).Returns
MPS_RES_OK
if successful, or another result code if not.If successful, the commit limit for
arena
is set tolimit
. The commit limit controls how much memory the MPS will obtain from the operating system. The commit limit cannot be set to a value that is lower than the number of bytes that the MPS is using. If an attempt is made to set the commit limit to a value greater than or equal to that returned bymps_arena_committed()
then it will succeed. If an attempt is made to set the commit limit to a value less than that returned bymps_arena_committed()
then it will succeed only if the amount committed by the MPS can be reduced by reducing the amount of spare committed memory; in such a case the spare committed memory will be reduced appropriately and the attempt will succeed.Note
mps_arena_commit_limit_set()
puts a limit on all memory committed by the MPS. The spare committed memory can be limited separately withmps_arena_spare_commit_limit_set()
. Note that “spare committed” memory is subject to both limits; there cannot be more spare committed memory than the spare commit limit, and there can’t be so much spare committed memory that there is more committed memory than the commit limit.
-
size_t
mps_arena_committed
(mps_arena_t arena)¶ Return the total committed memory for an arena.
arena
is the arena.Returns the total amount of memory that has been committed to RAM by the MPS, in bytes (1).
The committed memory is generally larger than the sum of the sizes of the allocated blocks. The reasons for this are:
some memory is used internally by the MPS to manage its own data structures and to record information about allocated blocks (such as free lists, page tables, colour tables, statistics, and so on);
operating systems (and hardware) typically restrict programs to requesting and releasing memory with a certain granularity (for example, pages), so extra memory is committed when this rounding is necessary;
there might also be spare committed memory: see
mps_arena_spare_committed()
.
The amount of committed memory is a good measure of how much virtual memory resource (“swap space”) the MPS is using from the operating system.
The function
mps_arena_committed()
may be called whatever state the the arena is in (unclamped, clamped, or parked). If it is called when the arena is in the unclamped state then the value may change after this function returns. A possible use might be to call it just aftermps_arena_collect()
to (over-)estimate the size of the heap.If you want to know how much memory the MPS is using then you’re probably interested in the value
mps_arena_committed() - mps_arena_spare_committed()
.The amount of committed memory can be limited with the function
mps_arena_commit_limit()
.
-
size_t
mps_arena_reserved
(mps_arena_t arena)¶ Return the total address space reserved by an arena, in bytes (1).
arena
is the arena.For a virtual memory arena, this is the total address space reserved via the operating system’s virtual memory interface.
For a client arena, this is the sum of the usable portions of the chunks of memory passed to the arena by the client program via
mps_arena_create()
andmps_arena_extend()
.Note
For a client arena, the reserved address may be lower than the sum of the
size
arguments passed tomps_arena_create()
andmps_arena_extend()
, because the arena may be unable to use the whole of each chunk for reasons of alignment.
-
size_t
mps_arena_spare_commit_limit
(mps_arena_t arena)¶ Return the current spare commit limit for an arena.
arena
is the arena to return the spare commit limit for.Returns the spare commit limit in bytes (1). The spare commit limit can be changed by calling
mps_arena_spare_commit_limit_set()
.
-
void
mps_arena_spare_commit_limit_set
(mps_arena_t arena, size_t limit)¶ Change the spare commit limit for an arena.
arena
is the arena to change the spare commit limit for.limit
is the new spare commit limit in bytes (1).The spare commit limit is the maximum amount of spare committed memory the MPS is allowed to have. Setting it to a value lower than the current amount of spare committed memory causes spare committed memory to be uncommitted so as to bring the value under the limit. In particular, setting it to 0 will mean that the MPS will have no spare committed memory.
Non-virtual-memory arena classes (for example, a client arena) do not have spare committed memory. For these arenas, this function functions sets a value but has no other effect.
Initially the spare commit limit is a configuration-dependent value. The value of the limit can be retrieved by the function
mps_arena_spare_commit_limit()
.
-
size_t
mps_arena_spare_committed
(mps_arena_t arena)¶ Return the total spare committed memory for an arena.
arena
is the arena.Returns the number of bytes of spare committed memory.
Spare committed memory is memory which the arena is managing as free memory (not in use by any pool and not otherwise in use for internal reasons) but which remains committed (mapped to RAM by the operating system). It is used by the arena to (attempt to) avoid calling the operating system to repeatedly map and unmap areas of virtual memory as the amount of memory in use goes up and down. Spare committed memory is counted as committed memory by
mps_arena_committed()
and is restricted bymps_arena_commit_limit()
.The amount of “spare committed” memory can be limited by calling
mps_arena_spare_commit_limit_set()
, and the value of that limit can be retrieved withmps_arena_spare_commit_limit()
. This is analogous to the functions for limiting the amount of committed memory.
3.4. Arena states¶
An arena is always in one of three states.
In the unclamped state, garbage collection may take place, objects may move in memory, references may be updated, location dependencies may become stale, virtual memory may be requested from or return to the operating system, and other kinds of background activity may occur. This is the normal state.
In the clamped state, objects do not move in memory, references do not change, the staleness of location dependencies does not change, and memory occupied by unreachable objects is not recycled.
However, a garbage collection may be in progress and incremental collection may still occur, but it will not be visible to the client program and no new collections will begin.
The parked state is the same as the clamped state, with the additional constraint that no garbage collections are in progress.
The clamped and parked states are used for introspection and debugging. If you are examining the contents of the heap, you don’t want data moving under your feet. So for example, if your program is stopped in GDB you might type:
(gdb) print mps_arena_clamp(arena)
before inspecting memory, and:
(gdb) print mps_arena_release(arena)
afterward.
The results of introspection functions like
mps_arena_has_addr()
only remain valid while the arena remains
in the parked state, and functions like mps_arena_roots_walk()
can only be called in this state.
-
void
mps_arena_clamp
(mps_arena_t arena)¶ Put an arena into the clamped state.
arena
is the arena to clamp.In the clamped state, no object motion will occur and the staleness of location dependencies will not change. All references to objects loaded while the arena is clamped will keep the same binary representation until after it is released by calling
mps_arena_release()
.In a clamped arena, incremental collection may still occur, but it will not be visible to the mutator and no new collections will begin. Space used by unreachable objects will not be recycled until the arena is unclamped.
-
void
mps_arena_park
(mps_arena_t arena)¶ Put an arena into the parked state.
arena
is the arena to park.While an arena is parked, no object motion will occur and the staleness of location dependencies will not change. All references to objects loaded while the arena is parked will keep the same binary representation until after it is released.
Any current collection is run to completion before the arena is parked, and no new collections will start. When an arena is in the parked state, it is necessarily not in the middle of a collection.
-
void
mps_arena_release
(mps_arena_t arena)¶ Puts an arena into the unclamped state.
arena
is the arena to unclamp.While an arena is unclamped, garbage collection, object motion, and other background activity can take place.
3.5. Running garbage collections¶
The Memory Pool System’s garbage collector runs asynchronously and incrementally. This means that it is not normally necessary to tell it when to start garbage collections, or to wait until it has finished collecting. (But if your program has idle time that could be productively spent by the MPS, see Using idle time for collection below.)
However, during development and testing it is useful to be able to request that MPS run a full collection cycle. For example, you might run frequent collections in an attempt to detect bugs in your allocation and scanning code.
-
void
mps_arena_collect
(mps_arena_t arena)¶ Collect an arena and put it into the parked state.
arena
is the arena to collect.The collector attempts to recycle as many unreachable objects as possible and reduce the size of the arena as much as possible (though in some cases it may increase because it becomes more fragmented). Note that the collector may not be able to recycle some objects (such as those near the destination of ambiguous references) even though they are not reachable.
If you do not want the arena to remain in the parked state, you must explicitly call
mps_arena_release()
afterwards.Note
It is not normally necessary to call this function: in the unclamped state, collections start automatically. However, it may be useful during development and debugging: the more frequently the collector runs, the sooner and more reliably errors are discovered. See General debugging advice.
-
mps_res_t
mps_arena_start_collect
(mps_arena_t arena)¶ Request an arena to start a full collection cycle.
arena
is the arena.Returns
MPS_RES_OK
if a collection is started, or another result code if not.This function puts
arena
into the unclamped state and requests that it start a full collection cycle. The call tomps_arena_start_collect()
returns quickly, leaving the collection to proceed incrementally (as for a collection that is scheduled automatically).Note
Contrast with
mps_arena_collect()
, which does not return until the collection has completed.
3.6. Using idle time for collection¶
Some types of program have “idle time” in which they are waiting for
an external event such as user input or network activity. The MPS
provides a function, mps_arena_step()
, for making use of idle
time to make memory management progress.
Here’s an example illustrating the use of this function in a program’s
event loop. When the program is idle (there are no client actions to
perform), it requests that the MPS spend up to 10 milliseconds on
incremental work, by calling mps_arena_step(arena, 0.010,
0.0)
. When this returns false to indicate that there is no more work
to do, the program blocks on the client for two seconds: if this times
out, it predicts that the user will remain idle for at least a further
second, so it calls mps_arena_step(arena, 0.010, 100.0)
to tell
that it’s a good time to start a collection taking up to 10 ms × 100
= 1 second, but not to pause for more than 10 ms.
The program remains responsive: the MPS doesn’t take control for more than a few milliseconds at a time (at most 10). But at the same time, major collection work can get done at times when the program would otherwise be idle. Of course the numbers here are only for illustration and should be chosen based on the requirements of the application.
for (;;) { /* event loop */
for (;;) {
if (client_is_waiting()) {
perform_client_action();
} else if (!mps_arena_step(arena, 0.010, 0.0)) {
/* no incremental MPS work remaining */
break;
}
}
if (!block_on_client_with_timeout(2.0)) {
/* Perhaps the user has gone for a cup of coffee? Allow the
* MPS to start a big piece of work, but don't actually pause
* for more than 10 ms. */
mps_arena_step(arena, 0.010, 100.0);
}
}
-
mps_bool_t
mps_arena_step
(mps_arena_t arena, double interval, double multiplier)¶ Request an arena to do some work during a period where the client program is idle.
arena
is the arena.interval
is the time, in seconds, the MPS is permitted to take. It must not be negative, but may be0.0
.multiplier
is the number of further similar calls that the client program expects to make during this idle period.Returns true if there was work for the MPS to do in
arena
(regardless of whether or not it did any) or false if there was nothing to do.mps_arena_step()
allows the client program to make use of idle time to do some garbage collection, for example when it is waiting for interactive input. The MPS makes every effort to return from this function withininterval
seconds, but cannot guarantee to do so, as it may need to call your own scanning code. It usesmultiplier
to decide whether to commence long-duration operations that consume CPU (such as a full collection): it will only start such an operation if it is expected to be completed withinmultiplier * interval
seconds.If the arena was in the parked state or the clamped state before
mps_arena_step()
was called, it is in the clamped state afterwards. It it was in the unclamped state, it remains there.
3.7. Arena introspection¶
Note
Introspection functions covered in other chapters are:
mps_addr_fmt()
: determine the object format to which an address belongs;mps_arena_formatted_objects_walk()
: visit all formatted objects in an arena;mps_arena_roots_walk()
: visit all references in roots registered with an arena; andmps_addr_pool()
: determine the pool to which an address belongs.
-
mps_bool_t
mps_arena_has_addr
(mps_arena_t arena, mps_addr_t addr)¶ Test whether an address is managed by an arena.
arena
is an arena.addr
is an address.Returns true if
addr
is managed byarena
; false otherwise.An arena manages a portion of address space. No two arenas overlap, so for any particular address this function will return true for at most one arena.
In general, not all addresses are managed by any arena. This is what allows the MPS to cooperate with other memory managers, shared object loaders, memory mapped file input/ouput, and so on: it does not steal the whole address space.
Note
The result from this function is valid only at the instant at which the function returned. In some circumstances the result may immediately become invalidated (for example, a garbage collection may occur, the address in question may become free, the arena may choose to unmap the address and return storage to the operating system). For reliable results call this function and interpret the result while the arena is in the parked state.
3.8. Protection interface¶
-
void
mps_arena_expose
(mps_arena_t arena)¶ Deprecated
starting with version 1.111.
Ensure that the MPS is not protecting any page in the arena with a read barrier or write barrier.
mps_arena
is the arena to expose.This is expected to only be useful for debugging. The arena is left in the clamped state.
Since barriers are used during a collection, calling this function has the same effect as calling
mps_arena_park()
: all collections are run to completion, and the arena is clamped so that no new collections begin. The MPS also uses barriers to maintain remembered sets, so calling this function will effectively destroy the remembered sets and any optimization gains from them.Calling this function is time-consuming: any active collections will be run to completion; and the next collection will have to recompute all the remembered sets by scanning the entire arena.
The recomputation of the remembered sets can be avoided by calling
mps_arena_unsafe_expose_remember_protection()
instead ofmps_arena_expose()
, and by callingmps_arena_unsafe_restore_protection()
before callingmps_arena_release()
. Those functions have unsafe aspects and place restrictions on what the client program can do (basically no exposed data can be changed).
-
void
mps_arena_unsafe_expose_remember_protection
(mps_arena_t arena)¶ Deprecated
starting with version 1.111.
Ensure that the MPS is not protecting any page in the arena with a read barrier or write barrier. In addition, request the MPS to remember some parts of its internal state so that they can be restored later.
mps_arena
is the arena to expose.This function is the same as
mps_arena_expose()
, but additionally causes the MPS to remember its protection state. The remembered protection state can optionally be restored later by calling themps_arena_unsafe_restore_protection()
function. This is an optimization that avoids the MPS having to recompute all the remembered sets by scanning the entire arena.However, restoring the remembered protections is only safe if the contents of the exposed pages have not been changed; therefore this function should only be used if you do not intend to change the pages, and the remembered protection must only be restored if the pages have not been changed.
The MPS will only remember the protection state if resources (memory) are available. If memory is low then only some or possibly none of the protection state will be remembered, with a corresponding necessity to recompute it later. The MPS provides no mechanism for the client program to determine whether the MPS has in fact remembered the protection state.
The remembered protection state, if any, is discarded after calling
mps_arena_unsafe_restore_protection()
, or as soon as the arena leaves the clamped state by callingmps_arena_release()
.
-
void
mps_arena_unsafe_restore_protection
(mps_arena_t arena)¶ Deprecated
starting with version 1.111.
Restore the remembered protection state for an arena.
mps_arena
is the arena to restore the protection state for.This function restores the protection state that the MPS has remembered when the client program called
mps_arena_unsafe_expose_remember_protection()
. The purpose of remembering and restoring the protection state is to avoid the need for the MPS to recompute all the remembered sets by scanning the entire arena, that occurs whenmps_arena_expose()
is used, and which causes the next garbage collection to be slow.The client program must not change the exposed data between the call to
mps_arena_unsafe_expose_remember_protection()
andmps_arena_unsafe_restore_protection()
. If the client program has changed the exposed data thenmps_arena_unsafe_restore_protection()
must not be called: in this case simply callmps_arena_release()
.Calling this function does not release the arena from the clamped state:
mps_arena_release()
must be called to continue normal collections.Calling this function causes the MPS to forget the remember protection state; as a consequence the same remembered state cannot be restored more than once.