Was hat Rust anstelle eines Müllsammlers?

92

Ich verstehe, dass Rust keinen Garbage Collector hat und frage mich, wie Speicher freigegeben wird, wenn eine Bindung außerhalb des Gültigkeitsbereichs liegt.

In diesem Beispiel verstehe ich, dass Rust den 'a' zugewiesenen Speicher zurückfordert, wenn er außerhalb des Gültigkeitsbereichs liegt.

{
    let a = 4
}

Das Problem, das ich damit habe, ist zum einen, wie das passiert, und zum anderen ist das nicht eine Art Müllsammlung? Wie unterscheidet es sich von der "typischen" Speicherbereinigung?

rix
quelle
11
"Deterministische Objektlebensdauern". Ähnlich wie C ++.
user2864740
@ user2864740 Dieser Leitfaden ist veraltet. Der moderne Ersatz wäre wahrscheinlich doc.rust-lang.org/book/references-and-borrowing.html .
Veedrac

Antworten:

72

Die Speicherbereinigung wird normalerweise regelmäßig oder bei Bedarf verwendet, z. B. wenn der Heap nahezu voll ist oder einen bestimmten Schwellenwert überschreitet. Es sucht dann nach nicht verwendeten Variablen und gibt je nach Algorithmus Speicher frei .

Rust würde wissen, wann die Variable den Gültigkeitsbereich verlässt oder ihre Lebensdauer zur Kompilierungszeit endet, und daher die entsprechenden LLVM- / Assembly-Anweisungen einfügen, um den Speicher freizugeben.

Rust erlaubt auch eine Art Garbage Collection, wie das Zählen von Atomreferenzen.

Ayonix
quelle
Durch Zuweisen von Speicher beim Einführen von Variablen und Freigeben von Speicher, wenn der Speicher nicht mehr benötigt wird? Ich weiß nicht wirklich, was du damit sagen willst. Vielleicht haben wir unterschiedliche Meinungen darüber, was ein GC dann ist.
Ayonix
1
Seine Frage ist, wie sich Rusts Ansatz von einem typischen GC unterscheidet. Also erklärte ich, was ein GC ist und wie Rust es ohne GC macht.
Ayonix
1
doc.rust-lang.org/book/the-stack-and-the-heap.html erklärt es ziemlich gut. Ja, viele Dinge sind im Stapel, aber geschweige denn kein ausreichender Indikator (siehe Kasten). Ich habe das der Einfachheit halber
weggelassen
1
@Amomum Eigentlich hat Rust keine gesalbte new()Funktion wie C, es sind nur statische Funktionen, und insbesondere so etwas wie das let x = MyStruct::new()Erstellen seines Objekts auf dem Stapel. Der eigentliche Indikator für die Heap-Zuordnung ist Box::new()(oder eine der Strukturen, die von Box abhängen).
Mario Carneiro
1
Welche anderen Sprachen behandeln die Speicherverwaltung ähnlich wie Rust?
still_dreaming_1
42

Die Grundidee beim Verwalten von Ressourcen (einschließlich Speicher) in einem Programm, unabhängig von der Strategie, besteht darin, dass die an nicht erreichbare "Objekte" gebundenen Ressourcen zurückgefordert werden können. Über den Speicher hinaus können diese Ressourcen Mutex-Sperren, Dateihandles, Sockets, Datenbankverbindungen ... sein.

Sprachen mit einem Garbage Collector durchsuchen den Speicher regelmäßig (auf die eine oder andere Weise), um nicht verwendete Objekte zu finden, die damit verbundenen Ressourcen freizugeben und schließlich den von diesen Objekten verwendeten Speicher freizugeben.

Rust hat keinen GC, wie geht das?

Rust hat Eigentum. Unter Verwendung eines affinen Typsystems verfolgt es, welche Variable noch an einem Objekt festhält, und ruft ihren Destruktor auf, wenn eine solche Variable den Gültigkeitsbereich verlässt. Sie können das affine Typsystem ziemlich leicht erkennen:

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

Ausbeuten:

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

Dies zeigt perfekt, dass zu jedem Zeitpunkt auf Sprachebene der Besitz nachverfolgt wird.

Dieser Besitz funktioniert rekursiv: Wenn Sie einen Vec<String>(dh ein dynamisches Array von Zeichenfolgen) haben, gehört jeder Stringdem, Vecder selbst einer Variablen oder einem anderen Objekt gehört, usw. Wenn eine Variable den Gültigkeitsbereich verlässt, es setzt rekursiv alle Ressourcen frei, die es besaß, auch indirekt. Im Falle der Vec<String>bedeutet dies:

  1. Freigeben des jedem zugeordneten Speicherpuffers String
  2. Freigeben des dem Vecselbst zugeordneten Speicherpuffers

Dank der Besitzverfolgung ist die Lebensdauer ALLER Programmobjekte streng an eine (oder mehrere) Funktionsvariablen gebunden, die letztendlich den Gültigkeitsbereich verlassen (wenn der Block, zu dem sie gehören, endet).

Hinweis: Dies ist etwas optimistisch, da durch Referenzzählung ( Rcoder Arc) Referenzzyklen gebildet werden können und somit Speicherlecks verursacht werden. In diesem Fall werden die an den Zyklus gebundenen Ressourcen möglicherweise nie freigegeben.

Matthieu M.
quelle
2
"Sprachen mit einem Garbage Collector scannen regelmäßig den Speicher (auf die eine oder andere Weise)". Viele tun es, aber das ist im Allgemeinen nicht wahr. Echtzeit-Garbage Collectors scannen eher inkrementell als periodisch. Referenzzählsprachen wie Mathematica scannen überhaupt nicht.
JD
@ JonHarrop: Ich zähle die Referenzzählung nicht als vollständigen Garbage Collection-Mechanismus, da sie ergänzt werden muss, um undichte Zyklen zu vermeiden. Was den inkrementellen / periodischen Unterschied betrifft, mag es meine schlechten Englischkenntnisse sein, aber ich sehe nicht, wie periodisch der inkrementelle Fall nicht abgedeckt wird ... Ich denke, dass das "(auf die eine oder andere Weise)" - Bit so viele verschiedene angemessen vermittelt Ansätze existieren. Wenn Sie die Garbage Collection besser beschreiben können, schlagen Sie dies auf jeden Fall vor. Ich habe jedoch nicht die Absicht, mich auf eine vollständige Erklärung einzulassen: Ich bin dafür nicht qualifiziert.
Matthieu M.
1
"Ich zähle die Referenzzählung nicht als vollständigen Garbage Collection-Mechanismus, da sie ergänzt werden muss, um Leckzyklen zu vermeiden." RC wird herkömmlicherweise als eine Form von GC angesehen. In Mathematica und Erlang können beispielsweise keine Zyklen vom Entwurf erstellt werden, sodass RC nicht leckt. Eine allgemeine
JD
@ JonHarrop: Richtig, wenn kein Zyklus möglich ist, kann RC nicht auslaufen.
Matthieu M.
2
"Ich sehe nicht, wie periodisch der inkrementelle Fall nicht abgedeckt wird". Stop the World-Algorithmen würden als periodisch angesehen, während die dreifarbige Markierung beispielsweise als inkrementell angesehen wird. Sie sind in diesem Zusammenhang Gegensätze.
JD
6

Bei einer Sprache, in der Sie den Speicher manuell verwalten müssen, wird die Unterscheidung zwischen dem Stapel und dem Heap kritisch. Jedes Mal, wenn Sie eine Funktion aufrufen, wird auf dem Stapel genügend Speicherplatz für alle Variablen zugewiesen, die im Bereich dieser Funktion enthalten sind. Wenn die Funktion zurückkehrt, wird der dieser Funktion zugeordnete Stapelrahmen vom Stapel "geknallt" und der Speicher für die zukünftige Verwendung freigegeben.

Aus praktischer Sicht wird diese versehentliche Speicherbereinigung als Mittel zur automatischen Speicherung von Speicher verwendet, der am Ende des Funktionsumfangs gelöscht wird.

Weitere Informationen finden Sie hier: https://doc.rust-lang.org/book/the-stack-and-the-heap.html

schweizerisch
quelle
3
Während die Verwendung des Stapels praktisch ist, können deterministische Objektlebensdauern trotzdem behandelt werden, wenn alle Werte auf dem Heap erstellt wurden. Somit ist es ein Implementierungsdetail; nicht unbedingt eine Sprachstrategie.
user2864740
2
Du benutzt dieses Wort weiter. Ich denke nicht, dass es bedeutet, was Sie denken, dass es bedeutet.
Schweizer
Bedeutet, was ich ausdrücken möchte ; das Gegenteil von nicht deterministischen Lebensdauern sein. Machen Sie ein Angebot für eine bessere Formulierung.
user2864740
Vielen Dank für die Antwort, ich habe die Punkte dem ersten gegeben, einfach weil es zuerst eingereicht wurde. Die Informationen sind ebenso nützlich und gültig.
Rix
@ user2864740 Deterministische Objektlebensdauern beziehen sich darauf, genau sagen zu können, wann der Speicher des Objekts gelöscht wird, sobald sein Destruktor aufgerufen wurde. Es hat nichts damit zu tun, wie dieser Destruktor überhaupt aufgerufen wird. Sie sprechen den gleichen Begriff immer wieder an, obwohl er für die Frage keine direkte Bedeutung hat.
Schweizer