Ответ 1
Попробуйте увеличить максимальный размер кеша кода с помощью следующей опции JVM:
-XX:ReservedCodeCacheSize=256m
См. мой ответ на другой вопрос для фона этого предложения.
Я сталкиваюсь с очень своеобразной проблемой. Мой tomcat отлично работает на 25% CPU 24/7, но через несколько дней мой процессор стреляет до 60%, и система останавливается и не восстанавливается.
Когда я беру дамп потока во время замедления, почти все потоки заняты какой-то строкой или связанной с ней операцией.
Нет ошибок OutOfMemory или любых исключений, все запросы все еще обрабатываются, но время отклика ухудшается до n-й степени, где даже второй секундный запрос замедляется до 60 секунд и более.
Конфигурация моего сервера следующая:
Ubuntu 12.04.2 LTS Linux 3.2.0-38-virtual #60-Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux
java version "1.7.0_13" Java(TM) SE Runtime Environment (build 1.7.0_13-b20) Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)
export JAVA_OPTS='-server -Xms18g -Xmx18g -XX:MaxPermSize=512m -XX:ThreadStackSize=512 -XX:NewRatio=1 -XX:SurvivorRatio=4 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled -Xloggc:/usr/tomcat/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.awt.headless=true'
Нажмите здесь, чтобы загрузить нить dump. Я удалил основную часть потоков и их стекеТех.
Нажмите здесь, чтобы загрузить журнал vmstat
Нажмите здесь, чтобы загрузить журнал gc
Любые идеи относительно причины этого? Благодаря
Попробуйте увеличить максимальный размер кеша кода с помощью следующей опции JVM:
-XX:ReservedCodeCacheSize=256m
См. мой ответ на другой вопрос для фона этого предложения.
Чтобы попытаться выявить оскорбительные запросы, вы можете настроить Stuck Thread Detection Valve в Tomcat.
Этот клапан позволяет обнаруживать запросы, которые требуют много времени для обработки, что может указывать на то, что поток, который обрабатывает его, застревает.
Когда такой запрос обнаружен, текущая трассировка стека его потока записывается в журнал Tomcat с уровнем WARN.
Идентификаторы и имена застрявших потоков доступны через JMX в атрибутах stuckThreadIds и stuckThreadNames. Идентификаторы могут использоваться со стандартным Threading JVM MBean (java.lang: type = Threading) для получения другой информации о каждой застрявшей нити.
Если загрузка процессора ниже 100%, и все же приложение остановлено, это означает, что что-то не позволяет полностью использовать процессор.
I/O или чрезмерное переключение контекста (например, вызванное блокировками) являются обычными виновниками этого.
Можете ли вы опубликовать вывод vmsstat 1 во время одного из событий? - следующий шаг в диагностике заключается в том, чтобы устранить, является ли проблема переключения контекста здесь.
Это не проблема памяти, так как на вашем дампе GC не занят и доступной памяти достаточно. Кроме того, CPU застрял на 60%, но если приложение будет занято вычислением (GC или что-то еще), он будет застрял бы на 100%, так же, если это была сетевая атака. Таким образом, источник этой проблемы должен включать в себя некоторую операцию с дисковым IO.
Известно, что Tomcat является ошибкой и имеет несколько серьезных проблем. Одна вещь, с которой я столкнулась, заключается в том, что без какой-либо конкретной причины Tomcat внезапно затопил свой собственный журнал с бессмысленными записями. Это не только заставило диск заполнить до 100%, но и значительно замедлило входящие запросы. Вы можете проверить это, взглянув на журналы tomcat и его размер.
Если это не источник, вы должны использовать доступные инструменты для проверить для любого странного диска-IO Tomcat и перейти оттуда.
Я думаю, что ваша проблема заключается в этом решении конфигурации -XX:PermSize=320m -XX:MaxPermSize=320m
, не позволяя вашему PemSpace динамически меняться, вы вызываете тупик, когда вы его исчерпаете, - помните, что межсетевой кеш использует PermSpace. Я попробую изменить -XX:MaxPermSize=320m
на -XX:MaxPermSize=512m
.
Существуют ли какие-либо аномалии в GC log? Кажется, что вы работаете с довольно большой кучей с некоторыми необычными опциями и делаете много строк, выделяющих материал. Возможно, вы страдаете от фрагментации кучи с течением времени (CMS не компактно). Также убедитесь, что никакая перестановка не происходит (может случиться, если куча слишком велика, поэтому ее редко посещает VM)
Я бы заподозрил, что это связано с GC, поскольку, по-видимому, поток не заблокирован. Вы пробовали с более недавним JDK? Также вы можете повторить попытку, но удалить несколько необычный вариант -XX: + CMSScavengeBeforeRemark, потому что у них может быть не так много тестового покрытия с каждой младшей версией JDK.
Другим подозрением могут быть входящие запросы с использованием странных кодировок (кириллических или арабских), которые приводят к чрезмерным накладкам массива Charset. Также проверьте, есть ли какой-нибудь робот на вашей странице, какие подозрительные запросы поступают? Для выяснения корневой операции манипуляции с строкой вам определенно требуется более длинная команда stacktraces.
Вам нужно использовать BTrace диагностику вызова метода.
Напишите запись script следующим образом:
com.xx.xx
класс префикса, которые вызывают String любым методом и время печати вызывают.@TLS
private static Map<String, Integer> countMap = BTraceUtils.newHashMap();
private static String prefix = "com.xx.xx";// package like com.xx.xx which you want to trace ()
@OnMethod(clazz = "java.lang.String", method = "/.*/") //all method in String
public static void traceMethodInvoke() {
String str = BTraceUtils.jstackStr();
for (String currentClass : str.split("\\n")) {
if (BTraceUtils.Strings.startsWith(currentClass, prefix)) {
if (!countMap.containsKey(currentClass)) {
countMap.put(currentClass, 1);
} else {
countMap.put(currentClass, countMap.get(currentClass) + 1);
}
break;
}
}
}
@OnTimer(5000)
public static void print() {
BTraceUtils.println("========================================");
for (Map.Entry<String, Integer> entry : countMap.entrySet()) {
if (entry.getValue() > 100) {// print if cont > 10
BTraceUtils.println(entry.getValue() + "\t\t" + entry.getKey());
}
}
BTraceUtils.println("===========================================");
}
Результат выводится следующим образом:
====================================================
1022 com.xx.xx.classA#m1
322 com.xx.xx.classA#m2
2022 com.xx.xx.classA#m21
422 com.xx.xx.ccc.classX#m11
522 com.xx.xx.zz.classS#m44
.........
prefix
для отслеживания другого префикса пакета.В результате вы можете проанализировать исходный код и выяснить проблемы.
Просканировав поток нитей, смотрящий на потоки RUNNABLE, выделяется одна вещь. Кажется, что ваша система обрабатывает или пытается обрабатывать большое количество запросов одновременно. И если у вас есть несколько ядер, вероятно, будет много времени нарезки. С другой стороны, я не вижу ясных → доказательств < что это связано с GC. (Но вы не включили журналы GC...)
Я предлагаю вам взглянуть на две вещи.
Если проблема связана с VM, то решение заключается в сокращении потребности в памяти приложения. Простой способ сделать это: уменьшить размер кучи Java.
Если проблема связана с нагрузкой, решить ее труднее:
Наконец, вы можете увидеть, помогает ли ему переключиться с CMS на Parallel Collector; обратитесь к странице настройки GC GC GC: Доступные коллекторы. Но я сомневаюсь, что это проблема GC.
Первое, что вы должны предпринять, - это выяснить, какие потоки фактически потребляют процессор. Это могут быть потоки, которые при выполнении строковых операций, или это может быть другой поток VM, который может выполнять операции GC и Sweep. Ссылка говорит о том, как соотносить всплески процессора с дампом потока
Как только вы можете указать точки, было бы более ясно, что должно быть следующим шагом вперед.
Надеюсь, что это поможет