Rust có gì thay vì một bộ thu gom rác?


95

Tôi hiểu Rust không có bộ thu gom rác và tôi đang tự hỏi làm cách nào để giải phóng bộ nhớ khi một ràng buộc vượt ra ngoài phạm vi.

Vì vậy, trong ví dụ này, tôi hiểu rằng Rust lấy lại bộ nhớ được cấp phát cho 'a' khi nó vượt ra khỏi phạm vi.

{
    let a = 4
}

Vấn đề tôi đang gặp phải với điều này, thứ nhất là điều này xảy ra như thế nào, và thứ hai đây không phải là một loại thu gom rác sao? Nó khác với thu gom rác 'điển hình' như thế nào?


12
"Các kiếp sống của đối tượng xác định". Tương tự như C ++.
user2864740,

@ user2864740 Hướng dẫn đó đã lỗi thời. Thay thế hiện đại có thể sẽ là doc.rust-lang.org/book/references-and-borrowing.html .
Veedrac

Câu trả lời:


74

Việc thu gom rác thường được sử dụng theo định kỳ hoặc theo yêu cầu, chẳng hạn như khi đống rác gần đầy hoặc trên ngưỡng nào đó. Sau đó, nó tìm kiếm các biến không sử dụng và giải phóng bộ nhớ của chúng, tùy thuộc vào thuật toán .

Rust sẽ biết khi nào biến vượt ra khỏi phạm vi hoặc thời gian tồn tại của nó kết thúc tại thời điểm biên dịch và do đó chèn các lệnh LLVM / assembly tương ứng để giải phóng bộ nhớ.

Rust cũng cho phép một số loại thu thập rác, chẳng hạn như đếm tham chiếu nguyên tử .


Bằng cách cấp phát bộ nhớ khi đưa vào biến và giải phóng bộ nhớ khi bộ nhớ không còn cần thiết? Tôi thực sự không biết bạn muốn nói gì với điều đó. Có thể chúng ta có những ý kiến ​​khác nhau về GC khi đó là gì.
Ayonix

1
Câu hỏi của anh ấy là cách tiếp cận của Rust khác với một GC điển hình như thế nào. Vì vậy, tôi đã giải thích GC là gì và cách Rust thực hiện nó mà không có GC.
Ayonix

1
doc.rust-lang.org/book/the-stack-and-the-heap.html giải thích nó khá tốt. Có, nhiều thứ nằm trong ngăn xếp nhưng không có chỉ báo nào đủ (xem Hộp). Tôi rời mà ra vì lợi ích của sự đơn giản, kể từ khi câu hỏi được yêu cầu nói chung mặc dù
Ayonix

1
@Amomum Thực ra Rust không có bất kỳ new()chức năng nào được xức dầu như C, chúng chỉ là các hàm tĩnh, và cụ thể là một cái gì đó như let x = MyStruct::new()tạo đối tượng của nó trên ngăn xếp. Các thực chỉ số phân bổ heap Box::new()(hoặc bất kỳ của các cấu trúc phụ thuộc vào Box).
Mario Carneiro

1
Những ngôn ngữ nào khác xử lý quản lý bộ nhớ theo cách tương tự như Rust?
still_dreaming_1 Ngày

43

Ý tưởng cơ bản của việc quản lý tài nguyên (bao gồm cả bộ nhớ) trong một chương trình, bất kể chiến lược nào, là các tài nguyên gắn với "đối tượng" không thể truy cập có thể được lấy lại. Ngoài bộ nhớ, những tài nguyên đó có thể là khóa mutex, xử lý tệp, ổ cắm, kết nối cơ sở dữ liệu ...

Các ngôn ngữ có bộ thu gom rác định kỳ quét bộ nhớ (cách này hay cách khác) để tìm các đối tượng không sử dụng, giải phóng tài nguyên liên quan đến chúng và cuối cùng giải phóng bộ nhớ được sử dụng bởi các đối tượng đó.

Rust không có GC, nó quản lý như thế nào?

Rust có quyền sở hữu. Sử dụng hệ thống kiểu affine , nó theo dõi biến nào vẫn đang giữ một đối tượng và khi một biến như vậy vượt ra khỏi phạm vi, nó sẽ gọi hàm hủy của nó. Bạn có thể thấy hệ thống kiểu affine có hiệu lực khá dễ dàng:

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

Sản lượng:

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

minh họa hoàn hảo rằng tại bất kỳ thời điểm nào, ở cấp độ ngôn ngữ, quyền sở hữu được theo dõi.

Quyền sở hữu này hoạt động theo cách đệ quy: nếu bạn có Vec<String>(tức là một mảng động gồm các chuỗi), thì mỗi chuỗi Stringthuộc quyền sở hữu của Vecchính nó thuộc sở hữu của một biến hoặc một đối tượng khác, v.v. do đó, khi một biến vượt ra ngoài phạm vi, nó giải phóng một cách đệ quy tất cả tài nguyên mà nó nắm giữ, thậm chí gián tiếp. Trong trường hợp Vec<String>này có nghĩa là:

  1. Giải phóng bộ đệm bộ nhớ liên quan đến từng String
  2. Giải phóng bộ đệm bộ nhớ được liên kết với Vecchính nó

Do đó, nhờ theo dõi quyền sở hữu, vòng đời của TẤT CẢ các đối tượng chương trình được ràng buộc chặt chẽ với một (hoặc một số) biến hàm, cuối cùng sẽ vượt ra khỏi phạm vi (khi khối mà chúng thuộc về kết thúc).

Lưu ý: điều này hơi lạc quan, sử dụng phép đếm tham chiếu ( Rchoặc Arc) có thể tạo thành các chu trình tham chiếu và do đó gây ra rò rỉ bộ nhớ, trong trường hợp đó, các tài nguyên gắn với chu trình có thể không bao giờ được giải phóng.


2
"Các ngôn ngữ có Trình thu gom rác định kỳ quét bộ nhớ (cách này hay cách khác)". Nhiều người làm nhưng điều đó không đúng. Bộ thu gom rác thời gian thực quét tăng dần thay vì định kỳ. Các ngôn ngữ đếm tham chiếu như Mathematica hoàn toàn không quét.
JD

@JonHarrop: Tôi không tính tham chiếu-đếm như một cơ chế Thu gom rác hoàn chỉnh vì nó phải được bổ sung để tránh rò rỉ chu trình. Đối với sự khác biệt gia tăng / chu kỳ, có thể trình độ tiếng Anh của tôi kém, nhưng tôi không thấy cách tuần hoàn không bao gồm trường hợp tăng dần ... Tôi nghĩ rằng bit "(cách này hay cách khác)" truyền đạt đầy đủ rằng nhiều các phương pháp tiếp cận tồn tại. Trong mọi trường hợp, nếu bạn có cách mô tả ngắn gọn hơn về Bộ sưu tập rác, vui lòng đề xuất. Tuy nhiên, tôi không có ý định tung ra một lời giải thích đầy ẩn ý: Tôi không đủ tiêu chuẩn để làm điều đó.
Matthieu M.

1
"Tôi không tính tham chiếu-đếm như một cơ chế Thu gom rác hoàn chỉnh vì nó phải được bổ sung để tránh rò rỉ chu trình". RC thường được coi là một dạng của GC. Ví dụ, trong Mathematica và Erlang, các chu trình không thể được tạo ra theo thiết kế để RC không bị rò rỉ. Để có quan điểm cấp cao, hãy xem "Lý thuyết thống nhất về thu gom rác" cs.virginia.edu/~cs415/reading/bacon-garbage.pdf
JD

@JonHarrop: Đúng, nếu không có chu kỳ thì RC không thể bị rò rỉ.
Matthieu M.

2
"Tôi không thấy cách định kỳ không bao gồm trường hợp gia tăng". Các thuật toán dừng trên thế giới sẽ được coi là định kỳ trong khi đánh dấu ba màu được coi là tăng dần, chẳng hạn. Chúng đối lập nhau trong bối cảnh này.
JD

6

Với một ngôn ngữ mà bạn phải quản lý bộ nhớ theo cách thủ công, sự khác biệt giữa ngăn xếp và đống trở nên quan trọng. Mỗi khi bạn gọi một hàm, đủ không gian được phân bổ trên ngăn xếp cho tất cả các biến có trong phạm vi của hàm đó. Khi hàm trả về, khung ngăn xếp được liên kết với hàm đó sẽ được "bật" ra khỏi ngăn xếp và bộ nhớ được giải phóng để sử dụng trong tương lai.

Từ quan điểm thực tế, việc dọn dẹp bộ nhớ vô tình này được sử dụng như một phương tiện lưu trữ bộ nhớ tự động sẽ bị xóa ở cuối phạm vi của chức năng.

Tham khảo thêm thông tin tại đây: https://doc.rust-lang.org/book/the-stack-and-the-heap.html


3
Mặc dù sử dụng ngăn xếp rất tiện dụng, nhưng vòng đời của đối tượng xác định vẫn có thể được xử lý nếu tất cả các giá trị được 'tạo trên heap'. Vì vậy, nó là một chi tiết thực hiện; không nhất thiết phải là một chiến lược ngôn ngữ.
user2864740

2
Bạn tiếp tục sử dụng từ đó. Tôi không nghĩ rằng nó có nghĩa là những gì bạn nghĩ nó có nghĩa là.
Swiss

Có nghĩa là những gì tôi muốn bày tỏ ; là đối lập với thời gian sống không xác định. Đưa ra đề nghị cho một cụm từ tốt hơn.
user2864740,

Cảm ơn câu trả lời, tôi đã cho điểm cho người đầu tiên đơn giản vì nó đã được gửi trước. Thông tin cũng hữu ích và hợp lệ.
rix

@ user2864740 Vòng đời của đối tượng xác định đề cập đến việc có thể cho biết chính xác thời điểm bộ nhớ của đối tượng sẽ bị xóa khi trình hủy của nó được gọi. Nó không liên quan gì đến cách mà trình hủy đó được gọi ngay từ đầu. Bạn cứ lặp đi lặp lại cùng một thuật ngữ mặc dù nó không có ý nghĩa trực tiếp đối với câu hỏi.
Thụy Sĩ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.