Понимание утечки загрузчика классов 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, а если нет, выделите больше.