本页目录

Allocator 与内存布局

Rust 默认用系统的 malloc/free,但可以通过 GlobalAlloc trait 替换全局分配器或给特定容器指定自定义分配器。Layout 描述内存的大小和对齐,Pin 保证值不可移动(对自引用类型和异步 Future 至关重要),#[repr(align)]#[repr(C)] 精确控制内存布局。

默认分配器

Rust 默认用系统的 malloc/free(glibc 的 ptmalloc2 on Linux),但可以替换。实现 GlobalAlloc trait 并标记 #[global_allocator]

use std::alloc::{GlobalAlloc, Layout, System};

struct MyAllocator;
unsafe impl GlobalAlloc for MyAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        // 实现分配逻辑: 从预分配的 buffer 切一块, 或调 mmap, ...
    }
    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        // 实现释放逻辑
    }
}

#[global_allocator]
static GLOBAL: MyAllocator = MyAllocator;

常用替代分配器:mimalloc(Microsoft,低碎片)、jemalloc(FreeBSD,多线程优化)、snmalloc(Microsoft Research,message-passing based)。

Layout: 分配请求的描述

分配器不直接接收"给我 N bytes"——而是接收 Layout 结构,包含大小和对齐:

use std::alloc::Layout;
let layout = Layout::new::<u64>();   // size=8, align=8
let layout2 = Layout::from_size_align(1024, 64).unwrap();

分配器必须返回满足对齐要求的内存。如果分配失败,返回 null(不是 panic)。

allocator API: 手动分配

use std::alloc::{alloc, dealloc, Layout};
let layout = Layout::new::<u64>();
let ptr = unsafe { alloc(layout) as *mut u64 };
unsafe { *ptr = 42 };
unsafe { dealloc(ptr as *mut u8, layout) };

这几乎是 C 的 malloc/free,加了类型安全的 Layout 参数。绝大多数代码不需要直接调 alloc——Box, Vec, String 等已处理。

Pin: 保证不被移动

Rust 的 move semantics 意味着任何值都可以被 memcpy 到新地址。但对于自引用结构(struct 的字段引用同一个 struct 的另一个字段),move 后引用变成悬垂。Pin 保证 pinned 的值不会再被 move:

use std::pin::Pin;
// 典型的自引用结构 (简化):
struct SelfRef { value: String, ptr: *const String }
// 构造时 ptr 指向 value, 但如果 SelfRef 被 move → ptr 悬垂
// Pin<Box<SelfRef>> 保证 SelfRef 不会被 move — 安全

async fn 生成的 Future 是典型的自引用结构——它引用了 await 点之前的局部变量。Pin<&mut Future> 保证在 poll 期间 Future 不会被 move。这是 async/await 编译器的内部实现基础。

参考

  • Rustonomicon: Allocator, Pin
  • RFC 1398: Custom allocators
  • RFC 2349: Pin

Keywords: GlobalAlloc, Layout, custom allocator, Pin, self-referential, #[repr(align)], alloc API