Как отлаживать ошибки памяти permgen для проекта, использующего фреймворк sbt и play 2 в Scala

Я использую sbt с достаточным объемом памяти и PermSize (см. значения ниже), но все еще получаю ошибки памяти.

[error] java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: PermGen space

Мой сценарий sbt выглядит следующим образом.

SBT_OPTS="-Xms1024M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=4028M" java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"

Я вношу изменения в IDE IntelliJ 13, а затем использую следующие команды для запуска моего приложения Play.

$ ./sbt
[name-of-my-play-project] $ compile
[name-of-my-play-project] $ ~run

Я также пробовал просто run вместо ~run. Я могу запустить проект и посетить страницы, если выполню холодный запуск, используя $./sbt, за которым следует ~run. Однако после нескольких изменений кода Play больше не отвечает.

Я попытался запустить тот же проект на другом компьютере (тоже OS-X), где я получаю похожее поведение. Меня беспокоит то, что это происходит не постоянно, но происходит достаточно часто, что делает меня непродуктивным, потому что мне приходится перезапускать sbt и играть с нуля, что полностью сводит на нет одно из ключевых преимуществ фреймворка Play2.

Я был бы признателен, если бы кто-нибудь мог помочь с тем, где я должен начать искать, чтобы определить, что не так с моей настройкой.

Я создал проект с помощью активатора, а затем начал использовать для него sbt.

ОБНОВЛЕНИЕ — снимок экрана из YourKit (по предложению @James), на котором показана утечка памяти.

Утечка памяти

ОБНОВЛЕНИЕ 2 YourKit показывает взаимоблокировку (см. прикрепленный снимок экрана). Я не уверен, что это основная причина проблемы. Я помещаю это здесь на всякий случай, если кто-то может дать больше информации о том, что происходит.

введите здесь описание изображения

ОБНОВЛЕНИЕ 3 — РЕШЕНИЕ После более чем 12 часов непрерывной отладки мне наконец удалось разобраться. Это была очень простая вещь, которую я объясню в конце. Поскольку в моем коде было много сторонних библиотек/плагинов (ReactiveMongo, Redis, ...), и я не был уверен, что что-то из этого вызывает ошибку OutOfMemory. Поэтому я просто удалил весь ненужный код, пока у меня не остался простой контроллер с одним методом, который просто возвращает простой «Hello world». Я все еще получаю ту же ошибку с этой простой программой. Это означало, что что-то не так с моей конфигурацией. Я попытался изменить версию sbt на 0.13.5 (sbt.version=0.13.5) в моем project/build.properties, и это все равно не помогло. Я скачал новый sbt-launch.jar (0.13.5), но проблема осталась. Опять же, одна вещь, которая оставалась неизменной, заключалась в том, что ошибка никогда не менялась. Так что по какой-то причине флаги для sbt не установлены.

Мой sbt скрипт bash выглядел так (ВСЕ В ОДНОЙ СТРОКЕ) -

SBT_OPTS="-Xms1024M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=4028M" java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"

я изменил его на

SBT_OPTS="-Xms1024M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=4028M" 
java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"

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


person Soumya Simanta    schedule 04.06.2014    source источник
comment
Используйте java 8 с G1 и без PermSize или java 7 с G1. См. раздел Выгрузка классов в сборщике мусора Java G1 (G1GC).   -  person Andrzej Jozwik    schedule 05.06.2014
comment
Существует утечка памяти, использование другой JVM или сборщика мусора не устранит утечку памяти, в Java 8 это просто означает, что системная память будет занимать до тех пор, пока она не закончится, и вся ваша система выйдет из строя.   -  person James Roper    schedule 05.06.2014


Ответы (1)


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

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

Если это не поможет, сделайте дамп кучи и загрузите его в YourKit. Теперь найдите несколько экземпляров классов, которые, как вы ожидаете, будут только одним из них или которых слишком много. Найдите объединенные корни GC для этого класса, и где-то в этой цепочке должен быть источник утечки памяти.

person James Roper    schedule 04.06.2014
comment
спасибо за ваше предложение. Я использовал Yourkit, который показал мне утечку памяти при загрузке класса (см. скриншот, прикрепленный выше). Я создавал свою собственную систему акторов в контроллере. Я заменил это на import play.libs.Akka, но это все равно не помогло. Я использую несколько других библиотек (reactivemongo, twitter4j), которые могут создавать потоки. Я разберусь с этим и попытаюсь остановить/очистить их на Global.onStop - person Soumya Simanta; 05.06.2014
comment
Скриншот, который вы там показали, не показывает утечку памяти, он просто показывает, что произошло при попытке выделить память (компилятор Scala), когда у вас закончилась память. В любом случае похоже, что вы решили эту проблему - утечки памяти не было, просто вы не выделили достаточно памяти JVM для загрузки классов из-за неправильной конфигурации. - person James Roper; 06.06.2014
comment
Я давал достаточно памяти. Проблема заключалась в том, что эти классы не выгружались, потому что этот флаг (-XX:+CMSClassUnloadingEnabled) не был установлен. У меня он работает отлично после этого изменения, и приятно не беспокоиться о перезапуске сервера после каждого небольшого изменения :-) ! - person Soumya Simanta; 06.06.2014