web-dev-qa-db-fra.com

Qu'est-ce que Rust a au lieu d'un ramasse-miettes?

Je comprends que Rust n’a pas de ramasse-miettes et me demande comment la mémoire est libérée quand une liaison sort du cadre.

Donc, dans cet exemple, je comprends que Rust récupère la mémoire allouée à 'a' quand elle sort de la portée.

{
    let a = 4
}

Le problème que j’éprouve avec cela est, premièrement, comment cela se produit, et deuxièmement, n’est-ce pas une sorte de ramassage des ordures? En quoi diffère-t-il de la collecte de déchets "typique"?

76
rix

Le ramassage des ordures est généralement utilisé périodiquement ou à la demande, par exemple si le tas est presque plein ou dépasse un certain seuil. Il recherche ensuite les variables non utilisées et libère leur mémoire en fonction de algorithme .

Rust saurait le moment où la variable sort de la portée ou lorsque sa durée de vie se termine au moment de la compilation et insère donc les instructions LLVM/Assembly correspondantes pour libérer la mémoire.

Rust permet également une sorte de récupération de place, comme décompte de références atomiques cependant.

60
Ayonix

L'idée de base de la gestion des ressources (y compris la mémoire) dans un programme, quelle que soit la stratégie, est que les ressources liées à des "objets" inaccessibles peuvent être récupérées. Au-delà de la mémoire, ces ressources peuvent être des verrous mutex, des descripteurs de fichiers, des sockets, des connexions à une base de données, etc.

Les langues avec un ramasse-miettes balayent périodiquement la mémoire (d'une manière ou d'une autre) pour rechercher des objets inutilisés, libèrent les ressources qui leur sont associées et enfin libèrent la mémoire utilisée par ces objets.

Rust n'a pas de GC, comment gère-t-il?

La rouille a la propriété. En utilisant un système de types affine , il enregistre la variable qui tient toujours sur un objet et, lorsque cette variable sort du cadre, appelle son destructeur. Vous pouvez facilement voir le système de types affine:

fn main() {
    let s: String = "Hello, World!".into();
    let t = s;
    println!("{}", s);
}

Rendements:

<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;
                     ^

ce qui illustre parfaitement que, à tout moment, au niveau de la langue, la propriété est suivie.

Cette propriété fonctionne de manière récursive: si vous avez un Vec<String> (c'est-à-dire un tableau dynamique de chaînes), alors chaque String appartient au Vec qui est lui-même la propriété d'une variable ou d'un autre objet, etc ... donc, quand une variable hors de portée, il libère récursivement toutes les ressources qu’il détenait, même indirectement. Dans le cas du Vec<String> ça signifie:

  1. Libérer la mémoire tampon associée à chaque String
  2. Libérer la mémoire tampon associée au Vec lui-même

Ainsi, grâce au suivi de propriété, la durée de vie de TOUS les objets de programme est strictement liée à une (ou plusieurs) variable de fonction, qui finira par sortir de la portée (lorsque le bloc auquel ils appartiennent se termine).

Note: c'est un peu optimiste, en utilisant le comptage de références (Rc ou Arc) il est possible de former des cycles de références et donc de provoquer des fuites de mémoire Dans ce cas, les ressources liées au cycle pourraient ne jamais être libérées .

38
Matthieu M.

Avec un langage dans lequel vous devez gérer manuellement la mémoire, la distinction entre la pile et le tas devient critique. Chaque fois que vous appelez une fonction, un espace suffisant est alloué sur la pile pour toutes les variables contenues dans l'étendue de cette fonction. Lorsque la fonction revient, le cadre de pile associé à cette fonction est "extrait" de la pile et la mémoire est libérée pour une utilisation ultérieure.

D'un point de vue pratique, ce nettoyage de mémoire par inadvertance est utilisé comme moyen de stockage en mémoire automatique qui sera effacé à la fin de la portée de la fonction.

Il y a plus d'informations disponibles ici: https://doc.Rust-lang.org/book/the-stack-and-the-heap.html

6
Swiss