java8 "java.lang.OutOfMemoryError: Metaspace"

После переключения JRE Java-приложения (сервисов, работающих на Tomcat) с Java 7 на Java 8 мы начали видеть java.lang.OutOfMemoryError: Metaspace после нескольких дней пробега с большим объемом трафика.

Использование кучи было в порядке. Metaspace перепрыгивает через некоторое время, когда во время тестирования производительности выполнялся один и тот же поток кода.

Какие могут быть возможные причины проблемы с памятью метапространства?

Текущие настройки:

-server -Xms8g -Xmx8g -XX:MaxMetaspaceSize=3200m  -XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC -XX:MaxGCPauseMillis=1000 
-XX:+DisableExplicitGC -XX:+PrintGCDetails 
-XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=7 -XX:NewSize=5004m 
-XX:MaxNewSize=5004m -XX:MaxTenuringThreshold=12 
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintFlagsFinal  
-XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution 
-XX:+PrintGCCause -XX:+PrintAdaptiveSizePolicy 
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=200M 

Также приложение имеет интенсивное использование отражения. Также мы используем пользовательский загрузчик классов. Все они работали нормально в Java 7.

2 ответа

Я предполагаю, что вы можете создать проблему с одним и тем же запросом (набором запросов) в течение определенного периода времени. Хорошо, что вы определили MaxMetaspaceSize, иначе приложение будет использовать собственную память до тех пор, пока она не исчерпает себя. Но я начну со следующих шагов:

  1. Проверьте, увеличивается ли количество классов, загружаемых в JVM, для одного и того же запроса, когда вы отправляете его на сервер несколько раз. Если да, возможно, вы создаете динамические классы, которые вызывают рост классов, загружаемых в метапространстве. Как проверить количество загруженных классов, вы можете использовать visualvm для подключения к серверу с использованием JMX или запустить локально для симуляции. Я упомяну шаги для локального, но для удаленного подключения JMX, вы должны добавить следующие параметры JVM к приложению и запустить его и удаленное подключение через порт 9999 и с -XX:+UnlockDiagnosticVMOptions.
   -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -XX:+UnlockDiagnosticVMOptions

Как только у вас есть visualvm (jvisualvm), подключенный к JVM, нажмите на монитор и посмотрите количество загруженных классов. Там вы можете следить за кучей и метапространством. Но я добавлю другие инструменты, чтобы внимательно следить за метапространством.

  1. Также, когда вы подключитесь к jvm, вы можете сделать снимок кучи и узнать классы, загруженные с использованием OQL. Таким образом, прежде чем вы получите дамп кучи, остановите запросы к серверу, чтобы не перехватывать какой-либо код запроса / выполнения в полете и связанные с ним объекты, но это не обязательно. Поэтому после многократного выполнения одного и того же набора запросов внутри visualvm в области "монитор" нажмите "Дамп кучи" справа вверху ". Затем откройте / загрузите снимок, и вы увидите опцию для консоли OQL. вы увидите некоторые предопределенные запросы OQL на правой нижней панели в разделе анализа permgen. Запустите запрос с именем "загруженная classloader гистограмма классов", я думаю, что это даст количество классов, загруженных каждым загрузчиком классов. Вы можете использовать его, чтобы узнать, какой загрузчик классов загружает классы.

выберите карту (sort(map(heap.objects('java.lang.ClassLoader')), '{loader: it, count: it.classes.elementCount }'), 'lhs.count"')

Но запрос выше, названный "classloader загруженный класс", будет медленным, что фактически покажет классы, загруженные каждым загрузчиком классов.

select { loader: cl,
             classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
    from instanceof java.lang.ClassLoader cl
  1. Затем попробуйте отследить рост в области метапространства. Теперь мы будем использовать jconsole и что-то новое, что есть в java: jmc (управление полетом java). Вы можете использовать jconsole для подключения к jvm (локальному или удаленному), и после того, как вы подключитесь, перейдите на вкладку памяти, и вы сможете следить за ростом без кучи, который должен иметь метапространство и кэш кода, а также сжатое пространство классов. А теперь подключите

JMC

чтобы подключиться к виртуальной машине, а затем, как только вы подключитесь, нажмите "Диагностические команды" в JMC, который находится справа вверху. Поскольку мы включили UnlockDiagnosticVMOptions, GC.class_stats может быть выполнен. Вы можете запустить его с показом всех столбцов и распечатать в CSV. Так что команда будет выглядеть так:

GC.class_stats -all=true -csv=true

И затем вы можете сравнить статистику классов за разные периоды и выяснить, какие классы вызывают проблемы (рост метапространства) или какие классы имеют связанную информацию (данные метода / метода) в метапространстве. Как проанализировать выходные данные csv, собранные за раз: ну, я бы взял этот csv и загрузил его в две аналогичные таблицы (представляющие csv) в базе данных или в каком-то другом месте для сравнения выходных данных GC.class_stats csv, где я могу запустить некоторый SQL или любые другие инструменты анализа. Это дало бы лучшее представление о том, что именно растет в метапространстве. Статистика класса GC имеет следующие столбцы:

Индекс, супер, InstSize,InstCount,InstBytes, зеркало,KlassBytes,K_secondary_supers,VTab,Itab,OopMap,IK_methods,IK_method_ordering,IK_default_methods,IK_default_vtable_indices,IK_local_interfaces,IK_transitive_interfaces,IK_fields,IK_inner_classes,IK_signers,class_annotations,class_type_annotations,fields_annotations,fields_type_annotations,methods_annotations,methods_parameter_annotations,methods_type_annotations,methods_default_annotations, аннотации,Cp,CpTags,CpCache,CpOperands,CpRefMap,CpAll,MethodCount,MethodBytes,ConstMethod,MethodData,StackMap, байт-код,MethodAll,ROAll,RWAll, Всего, ИмяКласса,ClassLoader

Надеюсь, поможет. Также кажется, что ошибка может быть в Java 8, если она не вызывает утечки в 1.7.

Также классы не будут выгружены из metaspace, если кто-либо удерживает какую-либо ссылку на загрузчик классов. Если вы знаете, что ваши загрузчики классов должны быть GCed, и никто не должен хранить ссылку на ваш загрузчик классов, вы можете вернуться к дампу кучи в visualvm, щелкнуть экземпляр загрузчика классов и щелкнуть правой кнопкой мыши, чтобы найти "ближайший корень GC", который скажет Вы, кто держит ссылку на загрузчики классов.

У нас была похожая проблема, и основная причина была в том, что файлы класса 60K загружаются в память метапространства, но ничего не выгружалось. Добавление ниже JVM arg устранило проблему.

-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true

https://issues.apache.org/jira/browse/CXF-2939

Надеюсь это поможет.

Также, если некоторые автоматически развертываются, например, tomcat, НЕ храните резервные копии в tomcat\webapps, в противном случае он может попытаться загрузить резервную копию и столкнуться с этими ресурсами.

Другие вопросы по тегам