深入理解内存管理:栈、堆与内存安全
1. 栈(Stack)
在程序运行中,每当调用一个函数或方法时,栈就会被用于为函数内部创建的值分配空间。函数中的所有 let
绑定要么作为值本身,要么作为指向堆上内存位置的指针,都会存储在栈中。这些值构成了活动函数的栈帧。
栈帧是栈中一个逻辑上的内存块,用于存储函数调用的上下文信息,可能包括函数参数、局部变量、返回地址以及在函数返回后需要恢复的寄存器值。随着越来越多的函数被调用,它们对应的栈帧会被压入栈中。当一个函数返回时,该函数对应的栈帧以及其中声明的所有值都会被移除,移除顺序与声明顺序相反,遵循后进先出(LIFO)原则。
栈上的内存分配速度很快,因为分配和释放内存只需要一条 CPU 指令,即增加或减少栈帧指针。栈帧指针(esp)是一个 CPU 寄存器,始终指向栈的顶部。当函数被调用或返回时,栈帧指针会不断更新。当函数返回时,通过将栈帧指针恢复到进入函数之前的位置,该函数的栈帧就会被丢弃。
栈是一种临时的内存分配策略,由于其简单性,在释放已使用的内存方面较为可靠。然而,这种特性使得栈不适合需要在当前栈帧之外长期存在的值的情况。
下面是一段代码,大致说明了在函数调用过程中栈是如何更新的:
// stack_basics.rs
fn double_of(b: i32) -> i32 {
let x = 2 * b;
x
}
fn main() {
let a = 12;
let result = double_of(a);
}