Держась за голову последовательности - PullRequest
10 голосов
/ 06 февраля 2010

Читая недавний вопрос, я определил обсуждаемую функцию

(def fib-seq
    (lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))

как держась за заголовок последовательности, но мне пришло в голову, перечитывая мой ответ, что я затмил детали, как будто они были очевидны, поэтому я вернулся, чтобы уточнить и потерпел неудачу. Я знаю, что fib-seq - это переменная, и пока она существует, она будет содержать все элементы последовательности, но я не совсем понимаю точную механику того, как именно эта последовательность сохраняется. Любые разъяснения будут оценены.

1 Ответ

12 голосов
/ 06 февраля 2010

В основном, применяются обычные правила GC ... Последовательность - это просто объект, и удерживание за его голову означает удерживание ссылки на этот объект. Это влечет за собой сохранение в памяти столько последовательности, сколько уже было реализовано, поскольку последовательности Clojure кэшируются.

(более подробное объяснение приведено ниже - см. Фрагмент, выделенный жирным шрифтом;; -))

'Последовательность' в Clojure - это объект, который реализует интерфейс ISeq. Это обеспечивает методы, которые извлекают первый элемент последовательности и остальную часть последовательности (другой объект, реализующий ISeq). В качестве ключевой детали они заботятся не только о том, чтобы вычислить правильный объект (первый / остаток последовательности) и вернуть его вызывающей стороне, но также и о кэшировании вычисленного значения в памяти, чтобы любые последующие запросы выполнялись быстрее - и, более того, важно, чтобы все запросы на один и тот же элемент последовательности гарантированно возвращали одно и то же значение, даже если ISeq генерируется поверх изменяемого объекта Java, который изменяется в какой-то момент. (Обратите внимание, что это абсолютно важно для неизменной семантики последовательностей Clojure.)

С другой стороны, Var - это контейнер, который в грубых выражениях содержит «указатель» на некоторый объект Java. Если это ISeq, то , если сам Var не является сборщиком мусора (чего, очевидно, никогда не будет, если это var верхнего уровня в существующем в настоящее время пространстве имен) или отскок, сам ISeq не будет собирать мусор и, в частности, память, которую он использует для кэширования первой / оставшейся части последовательности, не будет освобождена .

Что касается других элементов последовательности: «остаток» ISeq, связанный с Var, сам является ISeq. Кроме того, он кэшируется первым ISeq. Таким образом, первый элемент «остального» ISeq ISeq, привязанного к Var, никогда не будет собирать мусор, потому что ссылка на него удерживается «остальным» ISeq ISeq, привязанного к Var, и этот ISeq не будет быть GC'd, потому что он кэшируется как компонент rest объектом ISeq, связанным с Var, который, в свою очередь, не будет GC'd, пока он связан с Var, который, в свою очередь, обычно никогда не будет GC'd, потому что это Var верхнего уровня в пространстве имен.

Очевидно, что Var будет быть GC'd, если он перестает удерживаться его пространством имен (ns-unmap) или само пространство имен отбрасывается (remove-ns). Если случится, что он содержит ISeq, то этот ISeq будет GC'd, если и только если он не удерживается каким-либо другим фрагментом кода - конечно, применяются обычные правила GC. Для привязок, введенных с binding, и локальных привязок, введенных с let, все вышеизложенное относится к модулям проблем времени жизни привязок. (Которые не являются предметом этого вопроса.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...