Entiendo que Rust no tiene un recolector de basura y me pregunto cómo se libera la memoria cuando un enlace sale de su alcance.
Entonces, en este ejemplo, entiendo que Rust recupera la memoria asignada a 'a' cuando sale del alcance.
{
let a = 4
}
El problema que tengo con esto es, en primer lugar, cómo sucede esto y, en segundo lugar, ¿no es una especie de recolección de basura? ¿En qué se diferencia de la recolección de basura "típica"?
Respuestas:
La recolección de basura se usa normalmente de forma periódica o bajo demanda, como si el montón está casi lleno o por encima de algún umbral. Luego busca las variables no utilizadas y libera su memoria, según el algoritmo .
Rust sabría cuándo la variable sale del alcance o su vida útil termina en el tiempo de compilación y, por lo tanto, inserta las instrucciones LLVM / ensamblado correspondientes para liberar la memoria.
Rust también permite algún tipo de recolección de basura, como el conteo de referencias atómicas .
fuente
new()
función ungida como C, son solo funciones estáticas y, en particular, algo comolet x = MyStruct::new()
crea su objeto en la pila. El indicador real de asignación de montones esBox::new()
(o cualquiera de las estructuras que dependen de Box).La idea básica de administrar recursos (incluida la memoria) en un programa, cualquiera que sea la estrategia, es que los recursos vinculados a "objetos" inalcanzables se pueden recuperar. Más allá de la memoria, esos recursos pueden ser bloqueos mutex, identificadores de archivos, sockets, conexiones de bases de datos ...
Los lenguajes con un recolector de basura escanean periódicamente la memoria (de una forma u otra) para encontrar objetos no utilizados, liberar los recursos asociados con ellos y finalmente liberar la memoria utilizada por esos objetos.
Rust no tiene GC, ¿cómo se las arregla?
El óxido tiene la propiedad. Usando un sistema de tipos afines , rastrea qué variable todavía se aferra a un objeto y, cuando dicha variable sale del alcance, llama a su destructor. Puede ver el sistema de tipos afines en efecto con bastante facilidad:
fn main() { let s: String = "Hello, World!".into(); let t = s; println!("{}", s); }
Rendimientos:
<anon>:4:24: 4:25 error: use of moved value: `s` [E0382] <anon>:4 println!("{}", s); <anon>:3:13: 3:14 note: `s` moved here because it has type `collections::string::String`, which is moved by default <anon>:3 let t = s; ^
lo que ilustra perfectamente que en cualquier momento, a nivel del idioma, se realiza un seguimiento de la propiedad.
Esta propiedad funciona de forma recursiva: si tiene una
Vec<String>
(es decir, una matriz dinámica de cadenas), entonces cada unaString
es propiedad de laVec
que a su vez es propiedad de una variable u otro objeto, etc. de forma recursiva libera todos los recursos que tenía, incluso indirectamente. En el caso deVec<String>
esto significa:String
Vec
mismoPor lo tanto, gracias al seguimiento de la propiedad, la vida útil de TODOS los objetos del programa está estrictamente vinculada a una (o varias) variables de función, que finalmente saldrán del alcance (cuando finalice el bloque al que pertenecen).
Nota: esto es un poco optimista, utilizando el recuento de referencias (
Rc
oArc
) es posible formar ciclos de referencias y, por lo tanto, causar pérdidas de memoria, en cuyo caso los recursos vinculados al ciclo podrían no liberarse nunca.fuente
Con un lenguaje en el que debe administrar la memoria manualmente, la distinción entre la pila y el montón se vuelve crítica. Cada vez que llama a una función, se asigna suficiente espacio en la pila para todas las variables contenidas dentro del alcance de esa función. Cuando la función regresa, el marco de pila asociado con esa función se "saca" de la pila y la memoria se libera para uso futuro.
Desde un punto de vista práctico, esta limpieza de memoria inadvertida se utiliza como un medio de almacenamiento automático de memoria que se borrará al final del alcance de la función.
Hay más información disponible aquí: https://doc.rust-lang.org/book/the-stack-and-the-heap.html
fuente