Поиск кода, который заполняет PermGen с помощью мертвого кода Groovy
У нас был экземпляр нашей стеклянной рыбы каждые две недели вниз с java.lang.OutOfMemoryError: PermGen space
. Я увеличил пространство PermGen до 512 МБ и использование памяти сбрасывания памяти с помощью jstat -gc
. Через две недели я придумал следующий график, показывающий, как пространство PermGen постоянно увеличивается (единицы по оси x - минуты, ось y - KB).
![Graph of increasing PermGen usage]()
Я попробовал поискать какой-то инструмент для профилирования, который мог бы определить ошибку и поток здесь, на SO, упомянутый jmap, который оказался весьма полезным. Из приблизительно 14000 строк, сбрасываемых с jmap -permstats $PID
, приблизительно 12500 содержали groovy/lang/GroovyClassLoader$InnerLoader
, указывая на утечку памяти из нашего собственного кода Groovy или самого Groovy. Я должен указать, что Groovy составляет менее 1% соответствующей кодовой базы.
Пример ниже:
class_loader classes bytes parent_loader alive? type
<bootstrap> 3811 14830264 null live <internal>
0x00007f3aa7e19d20 20 164168 0x00007f3a9607f010 dead groovy/lang/[email protected]
0x00007f3aa7c850d0 20 164168 0x00007f3a9607f010 dead groovy/lang/[email protected]
0x00007f3aa5d15128 21 181072 0x00007f3a9607f010 dead groovy/lang/[email protected]
0x00007f3aad0b40e8 36 189816 0x00007f3a9d31fbf8 dead org/apache/jasper/servlet/[email protected]
....
Итак, как я могу узнать больше о том, какой код вызывает это?
Из в этой статье Я полагаю, что наш Groovy код динамически создает классы где-то. И из дампа из jmap я вижу, что большинство мертвых объектов/классов (?) Имеют один и тот же родительский загрузчик, хотя я не уверен, что это означает в этом контексте. Я не знаю, как исходить отсюда.
Добавление
Для опоздавших стоит отметить, что принятый ответ не устраняет проблему. Он просто продлевает период, необходимый до перезагрузки, в десять раз, не сохраняя столько информации о классе. На самом деле наши проблемы устраняли код, который его создал. Мы использовали рамки валидации (дизайн по контракту) OVal, где пользовательские ограничения script использовали Groovy как аннотации к методам и классы. Удаление аннотаций в пользу явных пред- и пост-условий в простой Java было скучным, но оно выполнило свою работу. Я подозреваю, что каждый раз, когда проверялось ограничение OVal, создавался новый анонимный класс, и каким-то образом связанные данные класса вызывали утечку памяти.
Ответы
Ответ 1
У нас была аналогичная проблема (1 неделя между сбоями). Проблема состоит в том, что Groovy кэширует мета методы. Мы закончили использование этого кода на основе этой дискуссии и отчет об ошибке
GroovyClassLoader loader = new GroovyClassLoader();
Reader reader = new BufferedReader(clob.getCharacterStream());
GroovyCodeSource source = new GroovyCodeSource(reader, name, "xb3.Classifier");
Class<?> groovyClass = loader.parseClass(source);
Object possibleClass = groovyClass.newInstance();
if (expectedType.isAssignableFrom(possibleClass.getClass())) {
classifiers.put((T) possibleClass, name);
}
reader.close();
// Tell Groovy we don't need any meta
// information about these classes
GroovySystem.getMetaClassRegistry().removeMetaClass(possibleClass.getClass());
// Tell the loader to clear out it cache,
// this ensures the classes will be GC'd
loader.clearCache();
Ответ 2
Если вы используете Sun JVM, измените его для IBM JVM, он отлично работает, я надеюсь:)