Понимание утечки загрузчика классов Groovy/Grails

вчера я развернул свое первое приложение Grails (2.3.6) на dev-сервер и начал его мониторинг. Я только что получил автоматический монитор, заявляющий, что CPU был закреплен на этой машине, и поэтому я пролил на него. Я побежал!--0--> и обнаружил, что это был PID моего Java-приложения, который закреплял сервер. Я также заметил, что память на 40%. Через несколько секунд процессор перестал закрепляться, опустился до нормального уровня, а память вернулась в диапазон ~20%. Классический major GC.

пока он был собирая, я сделал кучу свалки. После GC я открыл дамп в JVisualVM и увидел, что большая часть памяти выделяется для org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry класса. В общей сложности было почти 250 000 экземпляров, потребляющих около 25 Мб памяти.

я погуглил этот класс и посмотрел на него ультра полезные Javadocs. Так что я до сих пор понятия не имею, чем занимается этот класс.

но googling он также поднял около десятка или около того связанных статей (некоторые из них так вопросы) с участием этого класса и утечки PermGen/classloader с граалями / Groovy apps. И хотя кажется, что мое приложение действительно очистило этот экземпляр 250K с помощью GC, все еще беспокоит, что было так много экземпляров этого, и что GC закрепил CPU более 5 минут.

мои вопросы:

  • что это за класс и что с ним делает Groovy?
  • может кто-нибудь объяснить ответ для меня? Зачем -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled помочь в этом конкретная проблема?
  • почему этот класс особенно хлопотно для PermGen?

1 ответов


Groovy-это динамический язык, каждый вызов метода динамически отправляется. Чтобы оптимизировать, что Groovy создает MetaClass для каждого java.lang.Class на MetaClassRegistry. Эти MetaClass экземпляры создаются по требованию и сохраняются с использованием слабых ссылок.

причина, по которой вы видите много org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry это потому, что Groovy хранит карту классов и методов в памяти, чтобы они могли быть быстро отправлены средой выполнения. В зависимости от размера приложения, это может быть как у вас обнаружены тысячи классов, так как каждый класс может иметь десятки, а иногда и сотни методов.

однако в Groovy и Grails нет" утечки памяти", то, что вы видите, является нормальным поведением. Ваше приложение работает на низком уровне памяти, вероятно, потому, что он не был выделен достаточно памяти, это в свою очередь вызывает MetaClass экземпляров, чтобы быть собранным "мусор". Теперь скажем, например, у вас есть цикл:

for(str in strings) {
   println str.toUpperCase()
}

в этом случае мы вызываем метод на String класс. Если у вас мало памяти, что произойдет, так это то, что для каждой итерации цикла MetaClass будет собран мусор, а затем воссоздан снова для следующей итерации. Это может значительно замедлить работу приложения и привести к закреплению процессора, как вы видели. Это состояние обычно называется "отток метакласса" и является признаком того, что ваше приложение работает с низкой памятью кучи.

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

Что касается другого ответа, который вы ссылаетесь to, добавление разгрузки класса и настроек PermGen фактически ничего не сделает для решения ваших проблем с памятью если вы динамически разбираете классы во время выполнения. Пространство PermGen используется JVM для хранения динамически создаваемых классов. Groovy позволяет компилировать классы во время выполнения с помощью GroovyClassLoader.parseClass или GroovyShell.evaluate. Если вы постоянно анализируете классы, то да, добавление флагов выгрузки классов может помочь. См. Также этот пост:

размещая код который заполняет PermGen с dead Groovy code

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

вы должны проверить, выделили ли вы достаточно памяти кучи с помощью флага-Xmx, а если нет, выделите больше.