Я загружаю большое количество скриптов Groovy (2.4.6) и запускаю их с помощью GroovyScriptEngineImpl в моем приложении Java 8, и через некоторое время у меня возникает проблема.Java/Groovy - утечка памяти в GroovyClassLoader
Существует несколько вещей, которые вы должны знать:
- Я должен воссоздать новый
GroovyScriptEngineImpl
каждый раз, когда я запустить скрипт - Я должен воссоздать новый
GroovyClassLoader
каждый раз, когда я бегу сценарий
Мне нужно сделать это так, чтобы изолировать каждый скрипт в отдельной «среде»: я загружаю некоторые внешние JAR-файлы в загрузчик классов для некоторых скриптов, и я не хочу, чтобы другие скрипты могли использовать классы в ose JAR, когда они исполняются.
Проблема связана с тем, что для каждого скрипта, который я запускаю, GroovyClassLoader
создаст новый класс ScriptXXXX
и загрузит его, но никогда не выгрузит его.
Это приводит к тому, что количество классов, загружаемых неограниченно увеличиваться, и память заканчивается полностью заполненной.
Я перепробовала огромное количество различных решений, но ни один, кажется, работают:
- Добавление
-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
в аргументы виртуальной машины Java - Добавление
-Dgroovy.use.classvalue=true
в аргументы виртуальной машины Java - Удаление «мета-классы» созданный для каждого класса
ScriptXXXX
, как показано здесь: Groovy Classes not being collected but not signs of memory leak - Очистка кеша и закрытие
GroovyClassLoader
- Использование самоанализа вручную очистить некоторые поля кэширования классов в
GroovyScriptEngineImpl
- и т.д ...
Вот «Кратчайший путь к ГК» для одного из ScriptXXXX
класса в анализаторе памяти Eclipse:
У меня явно не хватает решений здесь, и ни один из них не работает, поскольку загрузчик классов всегда ссылается на классы, которые никогда не получают GCed.
Если вы хотите воспроизвести этот вопрос, вот пример кода:
GroovyScriptEngineImpl se;
while (true)
{
se = new GroovyScriptEngineImpl(new GroovyClassLoader());
CompiledScript script = se.compile("println(\"hello\")");
script.eval(se.createBindings());
}
Благодаря
UPDATE: После прочтения ответа pczeus, я попытался ограничить Метапространство, и некоторые классы, кажется, разгрузка действительно, и я думаю, что это классы ScriptXXX
.
При этом через несколько минут я получаю ошибки Out of Metaspace
во время выполнения скрипта.
Вот профиль я получаю с VisualVM:
И «Путь к ОМУ» в анализаторе памяти Eclipse, ибо ScriptXXX
классов действительно пусты (их больше нет экземпляра классов), даже класс по-прежнему указан в гистограмме.
Сколько стоит «большое количество» скриптов? – cjstehno
Потенциально неограниченный. На самом деле их может быть только несколько разных сценариев, но, как я сказал, они должны иметь изолированную среду, и поэтому их перекомпилируют каждый раз. В качестве примера я провел тест в течение 1 часа, который создал около 10000 скриптов и использовал 1 ГБ ОЗУ. И даже при меньшем количестве скриптов приложение будет сервером, который не следует перезапускать слишком часто, поэтому количество классов ScriptXXX будет расти в любом случае. – AntoineB
Что вы хотите сказать, хотите изолировать каждый скрипт в отдельной среде? Если вы используете один и тот же кеш скрипта, то как две разные оценки будут влиять друг на друга? –