Clojure OutOfMemoryError - PullRequest
       9

Clojure OutOfMemoryError

1 голос
/ 12 ноября 2011

Я читаю о том, как ленивые последовательности могут вызывать OutOfMemoryError при использовании, скажем, loop / recur на больших последовательностях.Я пытаюсь загрузить файл 3MB из памяти, чтобы обработать его, и я думаю, что это происходит со мной.Но я не знаю, есть ли идиоматический способ исправить это.Я пытался вставить doall's, но тогда моя программа, похоже, не заканчивалась.Малые входные данные работают:

Малые входные данные (содержимое файла): AAABBBCCC Правильный вывод: ((65 65) (65 66) (66 66) (67 67) (67 67))

Код:

(def file-path "/Users/me/Desktop/temp/bob.txt")
;(def file-path "/Users/me/Downloads/3MB_song.m4a")

(def group-by-twos
  (fn [a-list]
    (let [first-two (fn [a-list] (list (take 2 a-list)))
          the-rest-after-two (fn [a-list] (rest (rest a-list)))
          only-two-left? (fn [a-list] (if (= (count a-list) 2) true false))]
      (loop [result '() rest-of-list a-list]
        (if (nil? rest-of-list)
          result
          (if (only-two-left? rest-of-list)
            (concat result (list rest-of-list))
            (recur (concat result (first-two rest-of-list))
                   (the-rest-after-two rest-of-list))))))))

(def get-the-file
  (fn [file-name-and-path]
   (let [the-file-pointer
           (new java.io.RandomAccessFile (new java.io.File file-name-and-path) "r")
         intermediate-array (byte-array (.length the-file-pointer))] ;reserve space for final length
      (.readFully the-file-pointer intermediate-array)
      (group-by-twos (seq intermediate-array)))))

(get-the-file file-path)

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

Ответы [ 3 ]

2 голосов
/ 12 ноября 2011

Я полагаю, что идиоматическое решение будет таким:

 (partition 2 (map int (slurp "/Users/me/Desktop/temp/bob.txt")))

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

Примечание: это проглотит последний символ, если размер файла нечетный.Не ясно, что вы ожидаете, если размер нечетный.Если вы хотите иметь последнее значение в своем собственном списке, вы можете использовать (partition 2 2 [] ... )

user=> (partition 2 (map int "ABCDE"))
((65 66) (67 68))
user=> (partition 2 2 [] (map int "ABCDE"))
((65 66) (67 68) (69))
2 голосов
/ 12 ноября 2011

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

Что вы можете сделать, это создать seq для содержимого файла, используя что-то вроде:

(def get-the-file
  (fn [file-name-and-path]
   (let [the-file-pointer
           (new java.io.RandomAccessFile (new java.io.File file-name-and-path) "r")
         file-len (.length the-file-pointer)] ;get file len
      (partition­ 2 (map (fn [_] (.readByte the-file-pointer)) (range file-len))))))

ПРИМЕЧАНИЕ. Я не пробовал, но надеюсьэто дает вам представление о ленивом чтении файлов

1 голос
/ 13 ноября 2011

Остерегайтесь закрытых структур данных при работе с большими объемами данных. (типичное приложение Clojure использует в два-три раза больше памяти, чем одно и то же приложение Java - последовательности стоят дорого). Если вы можете прочитать все данные в массив, сделайте это. Затем обработайте его, убедившись, что вы не сохраняете ссылку на какой-либо заголовок последовательности, чтобы обеспечить сборку мусора во время процесса.

Также строки намного больше, чем примитивы char. Одна строка символов - 26 байтов, а длина символа - 2 байта. Даже если вам не нравится использовать массивы, arraylist в несколько раз меньше, чем последовательность или вектор.

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