Что есть в Rust вместо сборщика мусора?

95

Я понимаю, что в Rust нет сборщика мусора, и мне интересно, как освобождается память, когда привязка выходит за рамки.

Итак, в этом примере я понимаю, что Rust освобождает память, выделенную для 'a', когда она выходит за пределы области видимости.

{
    let a = 4
}

Проблема, с которой я столкнулся, во-первых, как это происходит, а во-вторых, разве это не сборка мусора? Чем он отличается от «типичной» сборки мусора?

rix
источник
12
«Детерминированные времена жизни объектов». Аналогично C ++.
user2864740
@ user2864740 Это руководство устарело. Современной заменой, вероятно, будет doc.rust-lang.org/book/references-and-borrowing.html .
Veedrac

Ответы:

74

Сборка мусора обычно используется периодически или по запросу, например, если куча почти заполнена или превышает некоторый порог. Затем он ищет неиспользуемые переменные и освобождает их память в зависимости от алгоритма .

Rust будет знать, когда переменная выходит за пределы области видимости или ее время жизни заканчивается во время компиляции, и, таким образом, вставляет соответствующие инструкции LLVM / сборки для освобождения памяти.

Rust также допускает некоторую сборку мусора, например, атомарный подсчет ссылок .

Ayonix
источник
Выделив память при вводе переменных и освободив память, когда память больше не нужна? Я действительно не знаю, что вы хотите этим сказать. Может, тогда у нас разные мнения о том, что такое GC.
Ayonix
1
Его вопрос заключается в том, чем подход Rust отличается от типичного GC. Итак, я объяснил, что такое сборщик мусора и как Rust делает это без сборщика мусора.
Ayonix
1
doc.rust-lang.org/book/the-stack-and-the-heap.html довольно хорошо это объясняет. Да, в стеке много вещей, но не говоря уже о недостаточном индикаторе (см. Вставку). Я оставил это для простоты, поскольку вопрос задавался в целом
Ayonix
1
@Amomum На самом деле Rust не имеет какой-либо помазанной new()функции, такой как C, это просто статические функции, и, в частности, что-то вроде let x = MyStruct::new()создания своего объекта в стеке. Реальный показатель кучного распределения является Box::new()(или какой - либо из структур , которые зависят от Box).
Марио Карнейро
1
Какие еще языки обрабатывают управление памятью так же, как Rust?
still_dreaming_1
43

Основная идея управления ресурсами (включая память) в программе, независимо от стратегии, заключается в том, что ресурсы, связанные с недостижимыми «объектами», могут быть возвращены. Помимо памяти, этими ресурсами могут быть блокировки мьютексов, дескрипторы файлов, сокеты, соединения с базой данных ...

Языки со сборщиком мусора периодически сканируют память (так или иначе), чтобы найти неиспользуемые объекты, освободить связанные с ними ресурсы и, наконец, освободить память, используемую этими объектами.

В Rust нет сборщика мусора, как он справляется?

У Rust есть право собственности. Используя систему аффинных типов , он отслеживает, какая переменная все еще хранится в объекте, и, когда такая переменная выходит за пределы области видимости, вызывает его деструктор. Вы можете легко увидеть, как действует система аффинных типов:

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

Урожайность:

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

что прекрасно иллюстрирует, что в любой момент времени, на уровне языка, право собственности отслеживается.

Это владение работает рекурсивно: если у вас есть Vec<String>(то есть динамический массив строк), то каждая Stringпринадлежит тому, Vecкоторый сам принадлежит переменной или другому объекту и т. Д., Таким образом, когда переменная выходит за пределы области видимости, он рекурсивно освобождает все ресурсы, которые он удерживал, даже косвенно. В случае с Vec<String>этим означает:

  1. Освобождение буфера памяти, связанного с каждым String
  2. Освобождение буфера памяти, связанного с Vecсамим собой

Таким образом, благодаря отслеживанию владения время жизни ВСЕХ программных объектов строго привязано к одной (или нескольким) функциональным переменным, которые в конечном итоге выйдут за пределы области видимости (когда блок, которому они принадлежат, завершится).

Примечание: это немного оптимистично, используя подсчет ссылок ( Rcили Arc) можно формировать циклы ссылок и, таким образом, вызывать утечки памяти, и в этом случае ресурсы, привязанные к циклу, могут никогда не быть освобождены.

Матье М.
источник
2
«Языки со сборщиком мусора периодически сканируют память (так или иначе)». Многие так и поступают, но в целом это неверно. Сборщики мусора в режиме реального времени сканируют не периодически, а постепенно. Языки подсчета ссылок, такие как Mathematica, вообще не сканируют.
JD
@JonHarrop: Я не считаю подсчет ссылок полным механизмом сборки мусора, поскольку он должен быть дополнен, чтобы избежать циклов утечки. Что касается возрастающей / периодической разницы, то, возможно, я плохо владею английским, но я не вижу, насколько периодическая не покрывает инкрементный случай ... Я думаю, что бит «(так или иначе)» адекватно передает такое количество разнообразных подходы существуют. В любом случае, если у вас есть лучший способ кратко описать сборку мусора, пожалуйста, не предлагайте. Я, однако, не собираюсь пускаться в полномасштабное объяснение: я неподходящий для этого.
Matthieu M.
1
«Я не считаю подсчет ссылок полным механизмом сборки мусора, так как он должен быть дополнен, чтобы избежать циклов утечки». RC принято рассматривать как разновидность GC. В Mathematica и Erlang, например, циклы не могут быть созданы заранее, поэтому RC не протекает. Для общего обзора
JD
@JonHarrop: Верно, если цикл невозможен, RC не может протекать.
Matthieu M.
2
«Я не понимаю, как периодичность не покрывает инкрементный случай». Алгоритмы Stop the World будут рассматриваться как периодические, в то время как трехцветная маркировка, например, рассматривается как инкрементная. В этом контексте они противоположны.
JD
6

В языке, где необходимо вручную управлять памятью, различие между стеком и кучей становится критическим. Каждый раз, когда вы вызываете функцию, в стеке выделяется достаточно места для всех переменных, содержащихся в области действия этой функции. Когда функция возвращается, кадр стека, связанный с этой функцией, «выталкивается» из стека, а память освобождается для использования в будущем.

С практической точки зрения эта непреднамеренная очистка памяти используется как средство автоматического сохранения памяти, которая будет очищена в конце области действия функции.

Дополнительная информация доступна здесь: https://doc.rust-lang.org/book/the-stack-and-the-heap.html

Швейцарский
источник
3
Хотя использование стека удобно, детерминированное время жизни объекта все же можно обрабатывать, если все значения были «созданы в куче». Таким образом, это деталь реализации; не обязательно языковая стратегия.
user2864740
2
Вы продолжаете использовать это слово. Я не думаю, что это означает то, что вы думаете.
Swiss
Означает то, что я хочу выразить ; будучи противоположностью недетерминированных времен жизни. Сделайте предложение для лучшей фразы.
user2864740
Спасибо за ответ, я поставил баллы первому просто потому, что он был отправлен первым. Информация столь же полезна и актуальна.
rix
@ user2864740 Детерминированное время жизни объекта означает возможность точно сказать, когда память объекта будет очищена после вызова его деструктора. Это не имеет ничего общего с тем, как этот деструктор вызывается в первую очередь. Вы постоянно повторяете один и тот же термин, даже если он не имеет прямого значения для вопроса.
Swiss