What is the difference between the allocators "Global" and "System"?

Viewed 542

The Rust standard library provides two allocator structs: std::alloc::{System, Global}. What's the relation and the difference between them?

1 Answers

Please note that the following explanations focus on Rust 1.60 nightly. Some things might change, when the new allocator_api feature gets stabilized.

TL;DR: std::alloc::System glues the standard library types that need memory allocations to the memory allocation mechanism of the operating system (such as: libc: malloc -> brk()/mmap()). alloc::alloc::Global is the default implementation for the allocator_api feature, which can be used to alter allocations of Vectors, Boxes, etc.


Whereas std::alloc::System actually comes from the standard library, std::alloc::Global is in fact a re-export from alloc::alloc::Global.

When you register a custom global allocator, you need a type that implements the trait core::alloc::GlobalAlloc. When you assign this type to a global static variable with the #[global_allocator] attribute, Rust lets the following "magic symbols" point to the corresponding implementations of the trait functions.

extern "Rust" {
    // These are the magic symbols to call the global allocator.  rustc generates
    // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute
    // (the code expanding that attribute macro generates those functions), or to call
    // the default implementations in libstd (`__rdl_alloc` etc. in `library/std/src/alloc.rs`)
    // otherwise.
    // The rustc fork of LLVM also special-cases these function names to be able to optimize them
    // like `malloc`, `realloc`, and `free`, respectively.
    #[rustc_allocator]
    #[rustc_allocator_nounwind]
    fn __rust_alloc(size: usize, align: usize) -> *mut u8;
    #[rustc_allocator_nounwind]
    fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
    #[rustc_allocator_nounwind]
    fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
    #[rustc_allocator_nounwind]
    fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
}

There exist higher-level Rust wrappers for these magic functions:
alloc::alloc::{alloc, alloc_zeroed, dealloc, realloc}.

std::alloc::System is the default #[global_allocator] allocator for the target you are compiling for (Unix, Windows, ...). For example, on UNIX-systems it will use libc::malloc, which will use mmap() and brk() syscalls to obtain heap memory from the operating system.

alloc::alloc::Global however is part of the allocator_api-feature which can be seen as a finer-grained selection of an allocator that should get used. The Allocator API brings the trait alloc::alloc::Allocator that gets implemented by alloc::alloc::Global. By default, it forwards requests to alloc::alloc::{alloc, alloc_zeroed, dealloc, realloc}. Typical types provided by the alloc crate (Vector, BTreeMap, Box, ...) all allow to chose a custom alloc::alloc::Allocator. The default value for this is alloc::alloc::Global.

To give an example, this is the type definition of alloc::vec::Vec (note the "Global):

pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
    buf: RawVec<T, A>,
    len: usize,
}

You can use custom allocators (the allocator_api-feature) for example to force page-aligned allocations in a Box<T>.

Related