Trait std::alloc::GlobalAlloc 1.28.0[−][src]
pub unsafe trait GlobalAlloc { unsafe fn alloc(&self, layout: Layout) -> *mut u8; unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { ... } unsafe fn realloc(
&self,
ptr: *mut u8,
layout: Layout,
new_size: usize
) -> *mut u8 { ... } }
Expand description
可以通过 #[global_allocator]
属性将其分配为标准库的默认内存分配器。
某些方法要求通过分配器 currently 分配存储块。这意味着:
-
该存储块的起始地址先前是由先前的调用返回到诸如
alloc
的分配方法的,并且 -
内存块尚未随后被释放,而是通过传递给诸如
dealloc
的释放方法或传递给返回非空指针的重新分配方法来对块进行释放。
Example
use std::alloc::{GlobalAlloc, Layout}; use std::cell::UnsafeCell; use std::ptr::null_mut; use std::sync::atomic::{ AtomicUsize, Ordering::{Acquire, SeqCst}, }; const ARENA_SIZE: usize = 128 * 1024; const MAX_SUPPORTED_ALIGN: usize = 4096; #[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN struct SimpleAllocator { arena: UnsafeCell<[u8; ARENA_SIZE]>, remaining: AtomicUsize, // 我们从顶部分配,倒计时 } #[global_allocator] static ALLOCATOR: SimpleAllocator = SimpleAllocator { arena: UnsafeCell::new([0x55; ARENA_SIZE]), remaining: AtomicUsize::new(ARENA_SIZE), }; unsafe impl Sync for SimpleAllocator {} unsafe impl GlobalAlloc for SimpleAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { let size = layout.size(); let align = layout.align(); // `Layout` 契约禁止使用 align=0 或 align 不是 2 的幂进行 `Layout`。 // 所以我们可以放心地使用掩码来确保对齐,而不必担心 UB。 let align_mask_to_round_down = !(align - 1); if align > MAX_SUPPORTED_ALIGN { return null_mut(); } let mut allocated = 0; if self .remaining .fetch_update(SeqCst, SeqCst, |mut remaining| { if size > remaining { return None; } remaining -= size; remaining &= align_mask_to_round_down; allocated = remaining; Some(remaining) }) .is_err() { return null_mut(); }; (self.arena.get() as *mut u8).add(allocated) } unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} } fn main() { let _s = format!("allocating a string!"); let currently = ALLOCATOR.remaining.load(Acquire); println!("allocated so far: {}", ARENA_SIZE - currently); }Run
Safety
由于多种原因,GlobalAlloc
trait 是 unsafe
trait,实现者必须确保遵守以下契约:
-
如果分配器解散,这是未定义的行为。可以在 future 中取消此限制,但是当前来自任何这些函数的 panic 都可能导致内存不安全。
-
Layout
查询和计算通常必须正确。允许 trait 的调用者依赖于每种方法上定义的协定,实现者必须确保此类协定保持正确。 -
即使源中存在显式堆分配,您也可能不依赖实际发生的分配。 优化器可能会检测到未使用的分配,该分配器可以将其完全消除或移到栈,因此从不调用分配器。 优化器可能进一步假设分配是无误的,因此由于分配器故障而导致分配器失败的代码现在可能突然起作用,因为优化器解决了分配需求。 更具体地说,无论您的自定义分配器是否允许计算发生了多少分配,下面的代码示例都是不正确的。
ⓘdrop(Box::new(42)); let number_of_heap_allocs = /* call private allocator API */; unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); }
Run请注意,上面提到的优化并不是唯一可以应用的优化。如果可以在不更改程序行为的情况下将其删除,则通常可能不依赖于发生的堆分配。 分配的发生与否不是程序行为的一部分,即使可以通过分配器检测到分配,该分配器通过打印或其他方式跟踪分配也会产生副作用。
Required methods
按照给定的 layout
分配内存。
返回指向新分配的内存的指针,或者返回 null 以指示分配失败。
Safety
该函数是不安全的,因为如果调用者不确保 layout
的大小为非零,则可能导致未定义的行为。
(扩展子特性可能提供行为的更具体限制,例如,保证响应零大小分配请求的前哨地址或空指针。)
分配的内存块可能会初始化也可能不会初始化。
Errors
返回空指针表示内存已耗尽,或者 layout
不满足此分配器的大小或对齐约束。
鼓励实现在内存耗尽时返回 null 而不是终止,但这不是严格的要求。 (具体来说: 在一个底层的原生分配库上实现此 trait 是 合法的,该本地分配库在内存耗尽时中止。)
鼓励希望因分配错误而终止计算的客户调用 handle_alloc_error
函数,而不是直接调用 panic!
或类似方法。
Provided methods
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8
[src]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8
[src]行为类似于 alloc
,但也确保在返回之前将内容设置为零。
Safety
出于与 alloc
相同的原因,此函数是不安全的。
但是,保证已分配的内存块将被初始化。
Errors
像 alloc
一样,返回空指针表示内存已耗尽或 layout
不满足分配器的大小或对齐约束。
鼓励希望因分配错误而终止计算的客户调用 handle_alloc_error
函数,而不是直接调用 panic!
或类似方法。
将内存块缩小或增加到给定的 new_size
。
该块由给定的 ptr
指针和 layout
描述。
如果返回非空指针,则 ptr
引用的内存块的所有权已转移到此分配器。
内存可能已释放,也可能尚未释放,应将其视为不可用的 (除非当然已通过此方法的返回值再次将其转移回调用方)。
layout
分配了新的存储块,但 size
更新为 new_size
。
当用 dealloc
释放新的内存块时,应该使用这种新的布局。
确保新存储块的范围 0..min(layout.size(), new_size)
与原始存储块具有相同的值。
如果此方法返回 null,则该存储块的所有权尚未转移到此分配器,并且该存储块的内容不会更改。
Safety
该函数是不安全的,因为如果调用者不能确保满足以下所有条件,则可能导致未定义的行为:
-
ptr
当前必须通过此分配器分配, -
layout
必须与用于分配该内存块的布局相同, -
new_size
必须大于零。 -
new_size
, 当四舍五入到最接近的layout.align()
倍数时,一定不能溢出 (即,四舍五入的值必须小于usize::MAX
)。
(扩展子特性可能提供行为的更具体限制,例如,保证响应零大小分配请求的前哨地址或空指针。)
Errors
如果新布局不符合分配器的大小和对齐约束,或者重新分配失败,则返回 null。
鼓励实现在内存耗尽时返回 null 而不是恐慌或终止,但是这不是严格的要求。 (具体来说: 在一个底层的原生分配库上实现此 trait 是 合法的,该本地分配库在内存耗尽时中止。)
鼓励希望响应重新分配错误而终止计算的客户调用 handle_alloc_error
函数,而不是直接调用 panic!
或类似方法。