在C/C++和其他低级语言中,开发者必须显式管理数据在内存中的放置:在栈(自动变量)或堆(通过malloc/new分配)。在这些语言中,内存泄漏、双重释放或使用未初始化或已删除的内存的错误经常发生。Rust通过所有权系统承担起严格的内存控制,在不使用垃圾收集器的情况下。
通过栈的自动内存管理很方便,但受限于大小(栈的深度)。在堆上分配内存需要显式管理资源,这很危险:可能会忘记释放内存或破坏指针的生存范围。垃圾收集器并不总是解决方案(资源开销、不确定的暂停)。内存管理错误会导致崩溃和漏洞。
在Rust中,栈和堆通过自动管理进行区分:所有值默认放置在栈中,而对于动态大小或长寿命的对象,则通过智能指针(例如,Box<T>,Vec<T>)使用堆。所有权和借用系统确保在传递所有权或结束资源的生存范围后,资源会自动释放。这一切都在编译阶段提供了保证的安全性,并且没有垃圾收集器引起的额外暂停。
代码示例:
fn main() { let a = 42; // 栈分配 let b = Box::new(42); // 堆分配 let mut v = Vec::new(); v.push(1); v.push(2); // 数组数据在堆中 }
关键特点:
可以手动释放(drop)栈上的内存吗?
不能。从栈分配的变量的释放在超出其作用域时会自动发生,手动进行drop既无用也不适用于栈指针。
移动(move)是否导致转移到堆上?
不。变量在所有者之间的移动并不一定将其转移到堆上,只是所有权发生了变化。
使用Box<T>是否保证T总是在堆上?
是的,Box<T>确实在堆上分配T,但所有权和生存范围仍然受到严格控制。
在项目中使用全局vector(Vec<T>)并手动实现清理,通过mem::forget或drop,有时留下悬挂指针指向被删除的内存。
优点:
缺点:
对象通过Box明确分配,数据传输遵循所有权规则,使用智能指针而不是返回对栈变量的引用。
优点:
缺点: