Rozumiem, że Rust nie ma odśmiecacza pamięci i zastanawiam się, jak zwalnia się pamięć, gdy powiązanie wykracza poza zakres.
W tym przykładzie rozumiem, że Rust odzyskuje pamięć przydzieloną do „a”, gdy wychodzi ona poza zakres.
{
let a = 4
}
Problem, który mam z tym, polega po pierwsze na tym, jak to się dzieje, a po drugie, czy nie jest to rodzaj czyszczenia pamięci? Czym różni się od „typowego” zbierania śmieci?
Odpowiedzi:
Wyrzucanie elementów bezużytecznych jest zwykle używane okresowo lub na żądanie, na przykład gdy sterta jest bliska zapełnienia lub przekracza pewien próg. Następnie szuka nieużywanych zmiennych i zwalnia ich pamięć, w zależności od algorytmu .
Rust wiedziałby, kiedy zmienna znajdzie się poza zakresem lub jej żywotność kończy się w czasie kompilacji i w ten sposób wstawia odpowiednie instrukcje LLVM / assemblera, aby zwolnić pamięć.
Rust pozwala także na zbieranie śmieci, na przykład atomowe liczenie odwołań .
źródło
new()
funkcji, takiej jak C, są to po prostu funkcje statyczne, aw szczególności coś takiego, jaklet x = MyStruct::new()
tworzy swój obiekt na stosie. Rzeczywistym wskaźnikiem sterty alokacji jestBox::new()
(lub dowolną ze struktur, które zależą Box).Podstawowa idea zarządzania zasobami (w tym pamięcią) w programie, bez względu na strategię, polega na tym, że zasoby powiązane z nieosiągalnymi „obiektami” mogą zostać odzyskane. Oprócz pamięci, te zasoby mogą być blokadami mutex, uchwytami plików, gniazdami, połączeniami z bazą danych ...
Języki z modułem odśmiecania pamięci okresowo skanują pamięć (w ten czy inny sposób) w celu znalezienia nieużywanych obiektów, zwalniania zasobów z nimi powiązanych i wreszcie zwalniania pamięci używanej przez te obiekty.
Rust nie ma GC, jak sobie z tym radzi?
Rust ma własność. Korzystając z systemu typu afinicznego , śledzi, która zmienna wciąż trzyma obiekt, a gdy taka zmienna wychodzi poza zakres, wywołuje jej destruktor. Możesz łatwo zobaczyć działający system typów afinicznych:
fn main() { let s: String = "Hello, World!".into(); let t = s; println!("{}", s); }
Plony:
<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; ^
co doskonale ilustruje, że w dowolnym momencie, na poziomie języka, własność jest śledzona.
Ta własność działa rekurencyjnie: jeśli masz a
Vec<String>
(tj. Dynamiczną tablicę ciągów), to każdaString
zVec
nich jest własnością zmiennej lub innego obiektu, itd ... tak więc, gdy zmienna wykracza poza zakres, rekurencyjnie zwalnia wszystkie posiadane zasoby, nawet pośrednio. W przypadkuVec<String>
tego oznacza:String
Vec
sobąW ten sposób, dzięki śledzeniu własności, czas życia WSZYSTKICH obiektów programu jest ściśle powiązany z jedną (lub kilkoma) zmiennymi funkcyjnymi, które ostatecznie wyjdą poza zakres (kiedy kończy się blok, do którego należą).
Uwaga: jest to nieco optymistyczne, używając liczenia referencji (
Rc
lubArc
) można tworzyć cykle odwołań i w ten sposób powodować wycieki pamięci, w którym to przypadku zasoby powiązane z cyklem mogą nigdy nie zostać zwolnione.źródło
W języku, w którym trzeba ręcznie zarządzać pamięcią, rozróżnienie między stosem a stertą staje się krytyczne. Za każdym razem, gdy wywołujesz funkcję, na stosie przydzielana jest wystarczająca ilość miejsca na wszystkie zmienne zawarte w zakresie tej funkcji. Kiedy funkcja zwraca, ramka stosu skojarzona z tą funkcją jest „zdejmowana” ze stosu, a pamięć jest zwalniana do wykorzystania w przyszłości.
Z praktycznego punktu widzenia to nieumyślne czyszczenie pamięci jest używane jako środek do automatycznego przechowywania pamięci, które zostanie wyczyszczone na końcu zakresu funkcji.
Więcej informacji można znaleźć tutaj: https://doc.rust-lang.org/book/the-stack-and-the-heap.html
źródło