Почему параллельные коллекции Scala иногда вызывают ошибку OutOfMemoryError?

Это займет около 1 секунды

(1 to 1000000).map(_+3)

Пока это дает java.lang.OutOfMemoryError: пространство кучи Java

(1 to 1000000).par.map(_+3)

РЕДАКТИРОВАТЬ

У меня стандартная конфигурация scala 2.9.2. Я набираю это в строке scala. И в bash я вижу [-n "$ JAVA_OPTS"] || JAVA_OPTS = "- Xmx256M -Xms32M"

И мне не нужно устанавливать JAVA_OPTS в моем окружении.

1 миллион целых чисел = 8 МБ, создание списка дважды = 16 МБ

 Christian01 июн. 2012 г., 11:45
@ Ян для меня это тоже хорошо работало в REPL, но только в первый раз ...
 Fabian01 июн. 2012 г., 11:51
Для меня это отлично работает. (Несколько раз...
 drexin01 июн. 2012 г., 11:36
Можете ли вы добавить немного больше деталей здесь? С каким объемом памяти вы запускаете свою JVM, любые другие коммутаторы? Вы запускаете это в REPL или компилируете? Если вы запустите это из REPL, сколько инструкций вы выполнили раньше? Я только попробовал это в своем REPL, и я выполнил это 10 раз без OOM.
 Jan01 июн. 2012 г., 11:41
Хорошо работает в моем РЕПЕЛЕ.
 drexin01 июн. 2012 г., 14:49
На 64-битном HotSpot целое в штучной упаковке имеет 24 байта * 1000000 == 24 МБ плюс накладные расходы на диапазон и параллельное содержимое ...

Ответы на вопрос(4)

Parallel коллекции не являются специализированными, поэтому объекты в штучной упаковке. Это означает, что вы не можете умножить количество элементов на 8, чтобы получить использование памяти.С помощьюmap означает, что диапазон преобразуется в вектор. Для параллельных векторов эффективная конкатенация еще не была реализована, поэтому объединение промежуточных векторов, созданных разными процессорами, происходит путем копирования, что требует больше памяти. Это будет решено в будущих выпусках. REPL сохраняет предыдущие результаты - объект, оцененный в каждой строке, остается в памяти.
 om-nom-nom01 июн. 2012 г., 15:50
Можно ли когда-нибудь специализировать их?
 axel2201 июн. 2012 г., 15:52
После того, как специализация массива реализована, некоторые типы коллекций должны быть специализированными, да.

но использование ThreadPool избавило меня от этой проблемы:

  val threadPool = Executors.newFixedThreadPool(4)
  val quadsMinPar = quadsMin.par
  quadsMinPar.tasksupport = new ThreadPoolTaskSupport(threadPool.asInstanceOf[ThreadPoolExecutor])

ForkJoin для больших коллекций может создавать слишком много потоков.

Это определенно связано с опцией памяти JVMа такж в память, необходимую для хранения коллекции Parralel. Например

scala> (1 to 1000000).par.map(_+3)

заканчиваетсяOutOfMemoryError в третий раз я пытался это оценить, а

scala> (1 to 1000000).par.map(_+3).seq

никогда не удалось. Проблема не в вычислениях, а в хранении коллекции Parallel.

объем памяти, необходимый для хранения параллельной коллекции, и объем памяти, необходимый для «прохождения» параллельной коллекции.

Разницу можно увидеть между этими двумя строками:

(1 to 1000000).map(_+3).toList
(1 to 1000000).par.map(_+3).toList

Помните, что REPL хранит вычисленные выражения. На моем REPL я могу выполнить оба из этих 7 раз, прежде чем у меня закончится память. Передача через параллельное выполнение временно использует дополнительную память, но как только ToList выполнен, это дополнительное использование - сборщик мусора.

(1 to 100000).par.map(_+3)

возвращает ParSeq [Int] (в данном случае ParVector), который занимает больше места, чем обычный вектор. Это я могу выполнить 4 раза, прежде чем мне не хватит памяти, тогда как я могу выполнить это:

(1 to 100000).map(_+3)

11 раз, прежде чем мне не хватит памяти. Так что параллельные коллекции, если вы будете хранить их, займут больше места.

В качестве обходного пути вы можете преобразовать их в более простые коллекции, такие какList прежде чем вернуть их.

Что касается того, почему параллельные коллекции занимают так много места и почему он хранит ссылки на так много вещей, я не знаю, но я подозреваюviews [*] и если ты думаешь, что это проблема, поднять вопрос для этого.

[*] без каких-либо реальных доказательств.

Ваш ответ на вопрос