2013-02-21 4 views
8

В this SO thread, я узнал, что сохранение ссылки на seq на большой коллекции предотвратит сбор всей коллекции от мусора.Когда следует избегать использования `seq` в Clojure?

Во-первых, эта тема с 2009 года. Это все еще верно в «современном» Clojure (v1.4.0 или v1.5.0)?

Во-вторых, относится ли эта проблема к ленивым последовательностям? Например, может ли (def s (drop 999 (seq (range 1000)))) разрешить сборщику мусора убирать первые 999 элементов последовательности?

Наконец, есть ли хороший способ обойти эту проблему для больших коллекций? Другими словами, если бы у меня был вектор, например, 10 миллионов элементов, я мог бы использовать вектор таким образом, чтобы потребляемые части могли быть собраны в мусор? А если бы у меня была хэш-карта с 10 миллионами элементов?

Причина, по которой я прошу, состоит в том, что я работаю на довольно больших наборах данных, и мне нужно быть более осторожными, чтобы не сохранять ссылки на объекты, так что объекты, которые мне не нужны, могут быть собраны в мусор. Как бы то ни было, в некоторых случаях я сталкиваюсь с ошибкой java.lang.OutOfMemoryError: GC overhead limit exceeded.

+0

Я думаю, что пример cgrand '(drop 999990 (vec (диапазон 1000000)))' обусловлен промежуточным вектором и поведением 'subvec'toring. Я не подозреваю, что ленивая последовательность 'cons'ed сделала бы это. Если вам нужно освободить вектор, сохранив подвектор, вы можете скопировать подвектор 'в' новый вектор. Очень интересный вопрос, хотя, я тоже жду ответа! –

ответ

6

Это всегда так, что если вы «держитесь за голову» последовательности, то Clojure будет вынужден хранить все в памяти. У него нет выбора: вы все еще держите ссылку на него.

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

См:

Если поставить фактическая нагрузка на элементы обрабатываемый, я подозреваю, вы увидите, что эта ошибка не будет больше. В этом случае вы можете легко обрабатывать ленивые последовательности, которые больше доступной памяти.

Конкретные коллекции, такие как векторы и хэшмапы, - это другое дело: они не ленивы, поэтому их всегда следует хранить в памяти. Если у вас есть наборы данных больше, чем память, то ваши варианты включают в себя:

  • Используйте ленивые последовательности и не останавливайтесь на голова
  • использования специализированных коллекций, которые поддерживают отложенную загрузку (Datomic использует некоторые структуры, как это я считаю)
  • Рассматривайте данные как поток событий (используя что-то вроде Storm)
  • Напишите собственный код, чтобы разделить данные на куски и обработать их по одному.
+0

Спасибо, mikera, это полезно. Я с нетерпением жду проверки Storm и Datomic. –

0

Если вы удерживаете главу последовательности в привязке, тогда вы правы, и она не может быть gc'd (и это с каждой версией Clojure). Если вы обрабатываете большое количество результатов, почему вам нужно держаться за голову?

Что касается дороги вокруг нее, да! реализация lazy-seq может gc-части, которые уже были обработаны и не имеют прямой ссылки из-за привязки. Просто убедитесь, что вы не держитесь за голову последовательности.

+1

Я думаю, что проблема в том, что некоторые подкатегории содержат ссылку на весь оригинал, даже если нет привязки к оригиналу? Документация 'subvec', например, читает« ... результирующая структура векторных акций с оригиналом и без обрезки ». Я полагаю, это означает, что «subvec» обертывает оригинал и просто переделывает смещения, но я не очень далеко зашел в состав Clojure. –

+0

@ A.Webb, я считаю, что вы правы и что вспомогательный вектор, созданный из приведенного выше примера, сохранит главу коллекции. (drop 999990 (lazy-seq (диапазон 1000000))), однако, не было (по крайней мере, из моего понимания) –

+0

Ничего волшебного об обертывании последовательности в 'lazy-seq' - это просто даст функцию run-once, возвращающую 'list *' обертка вокруг того же самого. (Кроме того, 'range' уже ленив.) –