Полный GC становится очень частым

У меня есть Java webapp, работающий на одном экземпляре tomcat. Во время пиков webapp обслуживает около 30 страниц в секунду и обычно около 15.

Моя среда:

O/S: SUSE Linux Enterprise Server 10 (x86_64)
RAM: 16GB

server: Tomcat 6.0.20
JVM: Java HotSpot(TM) 64-Bit Server VM 1.6.0_14
JVM options:
CATALINA_OPTS="-Xms512m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m
               -XX:+UseParallelGC
               -Djava.awt.headless=true
               -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
JAVA_OPTS="-server"

После нескольких дней безотказной работы Full GC начинает встречаться чаще, и это становится серьезной проблемой для доступности приложения. После перезапуска tomcat проблема исчезает, но, конечно, возвращается через 5-10 или 30 дней (несовместимо).

Полный журнал GC до и после перезагрузки находится на http://pastebin.com/raw.php?i=4NtkNXmi

Он показывает журнал перед перезагрузкой в ​​течение 6,6 дней, когда приложение страдает, потому что Full GC требуется 2,5 секунды и происходит каждые ~ 6 секунд.

Затем он показывает журнал сразу после перезагрузки, где Full GC происходит только каждые 5-10 минут.

У меня есть две дампы, использующие jmap -dump:format=b,file=dump.hprof PID, когда появляются полные GC (я не уверен, правильно ли я их получил, когда был получен полный GC или между 2 полными GC) и открыл их в http://www.eclipse.org/mat/, но не получил ничего полезного для подозреваемых в утечке:

  • 60MB: 1 экземпляр "org.hibernate.impl.SessionFactoryImpl" (я использую спящий режим с ehcache)
  • 80MB: 1,024 экземпляра "org.apache.tomcat.util.threads.ThreadWithAttributes" (это, вероятно, 1024 работника tomcat)
  • 45MB: 37 экземпляров "net.sf.ehcache.store.compound.impl.MemoryOnlyStore" (это должны быть мои 37 областей кэша в ehcache)

Обратите внимание, что я никогда не получаю OutOfMemoryError.

Любые идеи о том, где я должен смотреть дальше?

Ответ 1

Я переключился с -Xmx1024m на -Xmx2048m, и проблема исчезла. У меня теперь есть 100 дней безотказной работы.

Ответ 2

Когда у нас была эта проблема, мы в конечном итоге отследили ее до слишком маленького молодого поколения. Хотя мы дали много бара, молодому поколению не была предоставлена ​​справедливая доля.

Это означало, что небольшие сборки мусора случались бы чаще и приводили к тому, что некоторые молодые объекты были перемещены в поколение, что означало бы более крупные коллекции мусора.

Попробуйте использовать -XX:NewRatio с довольно низким значением (скажем, 2 или 3) и посмотрите, помогает ли это.

Более подробную информацию можно найти здесь.

Ответ 3

Что может произойти в вашем случае, так это то, что у вас много объектов, которые живут немного дольше, чем жизненный цикл NewGen. Если пространство для оставшихся в живых слишком мало, они идут прямо к OldGen. -XX:+PrintTenuringDistribution может дать некоторое представление. Ваш NewGen достаточно велик, поэтому попробуйте уменьшить SurvivorRatio.

также, jconsole, вероятно, предоставит более визуальное представление о том, что происходит с вашей памятью, попробуйте.

Ответ 4

Помимо настройки различных опций JVM, я также предлагаю перейти на более новую версию VM, потому что в более поздних версиях есть намного лучше настроенный сборщик мусора (также не пробовав новый экспериментальный).

Кроме того, если это (частично) истинно, что назначение большего количества бара для JVM может увеличить время, необходимое для выполнения GC, есть точка компромисса между использованием всего 16 ГБ памяти и увеличением вашей памяти, поэтому вы можете попробовать удвоить все значения, чтобы начать

Xms1024m -Xmx2048m -XX: PermSize = 256 м -XX: MaxPermSize = 512 м

Привет

Массимо