Почему возникают длительные задержки сбора мусора, почему?

Мне сложно справляться с проблемой сбора мусора Java и интерпретировать журналы.

Мое приложение требует, чтобы никакой GC не занимал больше 2 секунд, а в идеале - менее 100 мс.

Основываясь на некоторых предыдущих рекомендациях, я пытаюсь использовать следующие параметры командной строки:

 java -XX:MaxGCPauseMillis=100 -XX:NewRatio=9 -XX:+UseConcMarkSweepGC  -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -server -Xmx9g -Xms9g 

Приложение имеет большое количество долгосрочных хранимых объектов, которые хранятся в ConcurrentLinkedHashMap. Я вижу случайные длительные паузы, в худшем случае - до 10 секунд (это второй последний, как в журналах GC ниже)!

Вот некоторые из результатов, которые я получаю:

16938.968: [GC 16938.968: [ParNew: 153343K->17022K(153344K), 7.8608580 secs] 6184328K->6122510K(9420160K) icms_dc=7 , 7.8614100 secs] [Times: user=0.63 sys=0.01, real=7.86 secs] 
16947.087: [GC 16947.087: [ParNew: 153342K->17022K(153344K), 7.2604030 secs] 6258830K->6198642K(9420160K) icms_dc=7 , 7.2609780 secs] [Times: user=0.44 sys=0.00, real=7.27 secs] 
16954.614: [GC 16954.614: [ParNew: 153342K->17024K(153344K), 8.4307620 secs] 6334962K->6274625K(9420160K) icms_dc=7 , 8.4313150 secs] [Times: user=0.62 sys=0.01, real=8.43 secs] 
16963.310: [GC 16963.310: [ParNew: 153344K->17023K(153344K), 6.2588760 secs] 6410945K->6350748K(9420160K) icms_dc=7 , 6.2594290 secs] [Times: user=0.48 sys=0.01, real=6.25 secs] 
16969.834: [GC 16969.834: [ParNew: 153343K->17022K(153344K), 6.0274280 secs] 6487068K->6425868K(9420160K) icms_dc=7 , 6.0279830 secs] [Times: user=0.50 sys=0.01, real=6.03 secs] 
16976.122: [GC 16976.123: [ParNew: 153342K->17022K(153344K), 11.7774620 secs] 6562188K->6503030K(9420160K) icms_dc=7 , 11.7780180 secs] [Times: user=0.43 sys=0.04, real=11.78 secs] 
16988.164: [GC 16988.164: [ParNew: 153342K->17024K(153344K), 10.9477920 secs] 6639350K->6579928K(9420160K) icms_dc=7 , 10.9483440 secs] [Times: user=0.37 sys=0.02, real=10.95 secs] 
16999.371: [GC 16999.372: [ParNew: 153344K->17023K(153344K), 9.8828360 secs] 6716248K->6655886K(9420160K) icms_dc=7 , 9.8833940 secs] [Times: user=0.42 sys=0.01, real=9.88 secs] 
17009.509: [GC 17009.509: [ParNew: 153343K->17023K(153344K), 5.0699960 secs] 6792206K->6727987K(9420160K) icms_dc=7 , 5.0705660 secs] [Times: user=0.55 sys=0.01, real=5.07 secs] 
17014.838: [GC 17014.838: [ParNew: 153343K->17023K(153344K), 6.6411750 secs] 6864307K->6790974K(9420160K) icms_dc=7 , 6.6417400 secs] [Times: user=0.37 sys=0.01, real=6.63 secs] 
17021.735: [GC 17021.735: [ParNew: 153343K->17024K(153344K), 8.0545970 secs] 6927294K->6856409K(9420160K) icms_dc=7 , 8.0551790 secs] [Times: user=0.34 sys=0.03, real=8.05 secs] 
17030.052: [GC 17030.053: [ParNew: 153344K->17023K(153344K), 7.9756730 secs] 6992729K->6922569K(9420160K) icms_dc=7 , 7.9762530 secs] [Times: user=0.34 sys=0.01, real=7.98 secs] 
17038.398: [GC 17038.398: [ParNew: 153343K->17022K(153344K), 12.9613300 secs] 7058889K->6990725K(9420160K) icms_dc=7 , 12.9618850 secs] [Times: user=0.39 sys=0.01, real=12.96 secs] 
17051.630: [GC 17051.630: [ParNew: 153342K->17022K(153344K), 6.8942910 secs] 7127045K->7059607K(9420160K) icms_dc=7 , 6.8948380 secs] [Times: user=0.56 sys=0.02, real=6.89 secs] 
17058.798: [GC 17058.798: [ParNew: 153342K->17024K(153344K), 10.0262190 secs] 7195927K->7126351K(9420160K) icms_dc=7 , 10.0267860 secs] [Times: user=0.37 sys=0.01, real=10.02 secs] 
17069.096: [GC 17069.096: [ParNew: 153344K->17023K(153344K), 10.0419500 secs] 7262671K->7195002K(9420160K) icms_dc=7 , 10.0425020 secs] [Times: user=0.40 sys=0.02, real=10.04 secs] 
17079.410: [GC 17079.410: [ParNew: 153343K->17022K(153344K), 13.5389040 secs] 7331322K->7264275K(9420160K) icms_dc=7 , 13.5394610 secs] [Times: user=0.30 sys=0.01, real=13.54 secs] 
17093.223: [GC 17093.224: [ParNew: 153342K->17023K(153344K), 10.5909450 secs] 7400595K->7330446K(9420160K) icms_dc=7 , 10.5915060 secs] [Times: user=0.33 sys=0.00, real=10.58 secs] 
17104.083: [GC 17104.084: [ParNew: 153343K->17024K(153344K), 5.8420210 secs] 7466766K->7392173K(9420160K) icms_dc=7 , 5.8425920 secs] [Times: user=0.57 sys=0.00, real=5.84 secs] 

Я потратил несколько часов на различные веб-страницы, описывающие настройку Java GC, но никто из них не дал мне возможности интерпретировать журналы выше и придумать курс действий. Любые конкретные рекомендации, основанные на представленных мной журналах, будут очень благодарны.

Обновление:. На вопрос ниже:

Машина имеет 16 ГБ оперативной памяти, вот информация сверху:   Mem: 15483904k всего, 15280084k, 203820k бесплатно, буферы 155684k   Своп: 2031608k всего, 1347240k использовано, 684368k бесплатно, 3304044k кэш

Его другой запуск, но вот текущий верхний вывод для процесса:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  1016 sanity   17   0 10.2g 6.5g 9464 S    1 44.2  10:24.32 java                      

Обновление 2:. Более подробное ведение журнала, похоже, вызвало задержку в 400 мс:

{Heap before GC invocations=1331 (full 1):
 par new generation   total 153344K, used 153343K [0x00002aaaae200000, 0x00002aaab8860000, 0x00002aaab8860000)
  eden space 136320K, 100% used [0x00002aaaae200000, 0x00002aaab6720000, 0x00002aaab6720000)
  from space 17024K,  99% used [0x00002aaab77c0000, 0x00002aaab885fff0, 0x00002aaab8860000)
  to   space 17024K,   0% used [0x00002aaab6720000, 0x00002aaab6720000, 0x00002aaab77c0000)
 concurrent mark-sweep generation total 7169664K, used 4258496K [0x00002aaab8860000, 0x00002aac6e200000, 0x00002aac6e200000)
 concurrent-mark-sweep perm gen total 21248K, used 13269K [0x00002aac6e200000, 0x00002aac6f6c0000, 0x00002aac73600000)
484.738: [GC 484.738: [ParNew: 153343K->17022K(153344K), 0.3950480 secs] 4411840K->4341689K(7323008K), 0.3954820 secs] [Times: user=0.49 sys=0.07, real=0.40 secs] 
Heap after GC invocations=1332 (full 1):
 par new generation   total 153344K, used 17022K [0x00002aaaae200000, 0x00002aaab8860000, 0x00002aaab8860000)
  eden space 136320K,   0% used [0x00002aaaae200000, 0x00002aaaae200000, 0x00002aaab6720000)
  from space 17024K,  99% used [0x00002aaab6720000, 0x00002aaab77bfb68, 0x00002aaab77c0000)
  to   space 17024K,   0% used [0x00002aaab77c0000, 0x00002aaab77c0000, 0x00002aaab8860000)
 concurrent mark-sweep generation total 7169664K, used 4324666K [0x00002aaab8860000, 0x00002aac6e200000, 0x00002aac6e200000)
 concurrent-mark-sweep perm gen total 21248K, used 13269K [0x00002aac6e200000, 0x00002aac6f6c0000, 0x00002aac73600000)
}

Ответ 1

Оказывается, проблема заключалась в том, что куча выгружалась на диск, и задержка заключалась в том, что Java GC пришлось ждать, пока она будет заменена.

Решено (в основном), установив параметр Linux "swappiness" равным 0.

Ответ 2

От времени кажется, что GC фактически не работает все время (см. время пользователя), поэтому большую часть времени он ждет.

Просто дикая догадка: разве это не обмен? Сколько памяти у машины? Сколько из этого процесса получает java-процесс (размер резидентного набора)?

Изменить: почему он ждет:

Посмотрите на это (из вашей расшифровки)

[Times: user=0.39 sys=0.01, real=12.96 secs]

Это означает, что (я думаю, от начала до конца GC) прошло 12 (почти 13) секунд. Из этих 13 секунд 0,39 было потрачено на выполнение в пользовательском режиме, 0,01 было потрачено на запуск в режиме ядра. Если метод сбора времени не является полностью ошибочным (то есть числа действительно представляют время работы процесса/потока GC), это означает, что ожидается не менее 12 секунд.

Ответ 3

В целом, трудно получить настройку GC правильно, когда вам нужна такая большая куча.

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

Является ли ваша ConcurrentLinkedHashMap инициализирована всеми объектами постоянной памяти? Или он медленно растет, когда приложение продолжает работать? Если это последний, может быть трудно сократить время зависания GC, так как есть объекты, которые всегда выживают. Если это первое, вам нужно будет увеличить ваше поколение с размером ваших постоянных объектов + 20% или около того, и убедитесь, что ваш молодой ген достаточно велик, чтобы не отставать от переходных объектов, созданных в ходе ваше приложение.

Ответ 4

Возможно, ограничение производительности в 200 мс слишком строгое, и вам нужно самостоятельно управлять сборкой мусора? вы пробовали это с большим лимитом?

Ответ 5

Я думаю, что у вас есть этот UseConcMarkSweepGC и ошибка NewRatio. Поскольку ваше новое пространство не приближается к одной десятой -Jmx=9G. Ошибка включает в себя обходное решение (NewSize в абсолютном размере).

Еще один флаг, который может быть очень важен для вас, - CMSInitiatingOccupancyFraction. Он установлен на уровне 92% в java6 и составляет 68% в java5. Если старое пространство станет больше, CMS threadpool начнет выполнять свою работу. Если у вас есть процессор, который стоит потратить, нет никакой опасности для того, чтобы иметь живой набор, который находится выше начальной дроби.

Было бы неплохо, если бы вы включили статистику GC после исправления проблемы подкачки памяти.

Ответ 6

Если у вас есть строгие требования к срокам, возможно, вы должны зарегистрироваться в Java Real-Time System.

RTSJ/Java RTS предоставляет:

Набор API, семантические расширения Java VM и модификации уровней JVM-to-OS, которые позволяют разработчикам Java правильно рассуждать о временном поведении Java-приложений и контролировать их.

Ответ 7

Запустили ли вы свое приложение через профилировщик, чтобы увидеть, что на самом деле происходит то, что, по вашему мнению, происходит в отношении memeory?

Одна вещь, которую я делаю, когда смотрю на это, - это использовать профилировщик Netbeans (хотя любой профилировщик должен дать вам это), посмотрите на живые байты (распределение памяти) и посмотрите, есть ли те, которые имеют большое количество выделенные байты и выделенный objectd - это то, что я ожидаю и иду от того, где я ожидаю.

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

Ответ 8

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

Вы реализовали finalize() для любого из ваших объектов? Это вызовет большой штраф в ГК. Было бы также интересно увидеть тестовый прогон с кучей, возможно, 6 концертов, если вы получите непропорциональное повышение производительности, это будет означать, что GC обрушивается на память.

Ответ 9

Я думаю, ваше внимание может быть немного неверно направлено.

Проведите немного времени в профилировщике, найдя ваши самые большие точки доступа. Если в коде есть всего несколько мест, в которых происходит большинство ваших распределений, попробуйте использовать пул объектов, а не всегда создавать новые объекты.

Коллекционные классы и StringBuilders - отличные кандидаты для объединения. Когда вы вернете их в пул, вызовите методы collection.clear() или stringbuilder.setLength(0), чтобы они были готовы к потреблению, когда следующий вызывающий объект хочет извлечь их из пула.

Лучший способ настройки GC - создание меньшего количества объектов. Существует множество стратегий для устранения распределений, и объединение - это лишь один из них (хотя один из моих фаворитов).

ОБНОВЛЕНИЕ: прошло пять лет с тех пор, как я написал этот ответ, и мое мнение о пуле в основном изменилось. Вернувшись, когда я написал этот ответ в 2009 году, я часто мог использовать пул объектов (даже простых объектов, таких как StringBuilder), чтобы ускорить тесные внутренние циклы с большим количеством распределений. В наши дни сложнее найти случаи, когда объединение не ухудшает ситуацию. Я почти никогда не использую пулы для чего-либо, кроме потоков или соединений. Тем не менее, это хороший инструмент для вашего удобства, даже если вы его не используете часто.

Ответ 10

Можете ли вы отправить/ссылку на код реализации ConcurrentLinkedHashMap? Если это реализация, которую я опубликовал, пожалуйста, откройте билет на странице проекта, чтобы мы могли отлаживать его вместе. Если нет, то знание сведений о вашей реализации поможет определить, где может возникнуть проблема.

Ответ 11

9 GB JVM! Никогда не видел этого раньше! Я думаю, что ваши 10 сек. паузы вполне нормальные. см. это (возможно, вы уже прочитали это...)

Ответ 12

для меня проблема была полна оставшихся в живых. Поскольку CMS не является уплотненным, мусор был передан непосредственно в старый ген. это может быть очень дорого с такой большой кучей. Необходимо увеличить количество оставшихся в живых и MaxTenuringThreshold, чтобы максимально повысить продвижение по службе.

Алекс