Поиск кода, который заполняет 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, он отлично работает, я надеюсь:)