跳到主要内容

内存分配优化

问题

如何减少 Rust 程序的堆分配?

答案

常见优化技巧

// 1. 预分配容量
// ❌ 多次扩容
let mut v = Vec::new();
for i in 0..1000 {
v.push(i); // 可能触发多次 realloc
}

// ✅ 一次分配
let mut v = Vec::with_capacity(1000);
for i in 0..1000 {
v.push(i); // 无 realloc
}

// 2. 用栈代替堆
// ❌ 堆分配
let data: Box<[u8; 64]> = Box::new([0u8; 64]);

// ✅ 栈分配
let data = [0u8; 64];

// 3. 复用 String/Vec 缓冲区
let mut buf = String::with_capacity(256);
for item in items {
buf.clear(); // 清空但保留容量
write!(buf, "item: {}", item).unwrap();
process(&buf);
}

// 4. 使用 SmallVec(小数据栈上,大数据堆)
use smallvec::SmallVec;
let mut v: SmallVec<[i32; 8]> = SmallVec::new();
// 8 个以内在栈上,超过才堆分配

减少分配的常见模式

模式说明
Vec::with_capacity预分配
String::with_capacity预分配字符串
SmallVec / arrayvec小数据栈分配
&str 代替 String引用而非拥有
Cow<str>按需分配
对象池crossbeam::epoch

常见面试问题

Q1: 如何检测程序的堆分配次数?

答案

// 方法 1:dhat 分析器(详见性能分析章节)
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;

// 方法 2:自定义分配器统计
use std::alloc::{GlobalAlloc, System, Layout};
use std::sync::atomic::{AtomicUsize, Ordering};

static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);

struct CountingAllocator;
unsafe impl GlobalAlloc for CountingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
ALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
}

相关链接