Java ConculrentMarksweep мусорный сборщик не удаляет весь мусор
-
28-09-2019 - |
Вопрос
Краткая форма: сборщик мусора CMS, кажется, не может собрать постоянно растущее количество мусора; В конце концов, наш JVM заполняется, а приложение становится не отвечает. Принуждение GC через внешний инструмент (JConsole или jmap -histo:live
) очищает его один раз.
Обновление: проблема, по-видимому, связана с плагином JTOP для JConsole; Если мы не запустим jconsole, или запустите его без плагина Jтопа, поведение уходит.
(Технические примечания: мы запускаем Sun JDK 1.6.0_07, 32-разряд, на поле Linux 2.6.9. Обновление версии JDK не является обязательным вариантом, если нет неизбежной, основной причины. Кроме того, наша система не подключен к доступной интернету машины, поэтому скриншоты jconsole и т. Д. Не вариант.)
В настоящее время мы работаем наш JVM со следующими флагами:
-server -Xms3072m -Xmx3072m -XX:NewSize=512m -XX:MaxNewSize=512m
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=70
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-XX:+DisableExplicitGC
Наблюдая за графиком памяти в JConsole, есть полный GC, который проходит каждые ~ 15 минут или около того, в течение первых нескольких часов продолжительности жизни нашего приложения; После каждого полного GC все еще используются все еще и больше памяти. Через несколько часов система попадает в устойчивое состояние, где в старом Rece CMS приблизительно 2 ГБ памяти.
Что звучит как классическая утечка памяти, за исключением того, что если мы используем любой инструмент, который заставляет полный GC (ударяя кнопку «собирать мусор» в JConsole или работает jmap -histo:live
, И т. Д.) Старый ген внезапно упал на ~ 500 МБ, и наше приложение снова отдается в течение следующих нескольких часов (в течение которого продолжается тот же шаблон - после каждого полного GC, все больше и больше старого поколения заполнены.)
Одно следует примечание: в jconsole, сообщаемое CONCURRENTMARMSWEEP GC Count будет оставаться в 0, пока не принудим GC с JConsole / jmap / etc.
С использованием jmap -histo
а также jmap -histo:live
В последовательности я могу определить, что видимо необъяснимые объекты состоят из:
- несколько миллионов
HashMap
S и массивыHashMap$Entry
(в соотношении 1: 1) - несколько миллионов
Vector
S и объектные массивы (1: 1 соотношение и примерно так же, как количество хэсмапов) - несколько миллионов
HashSet
,Hashtable
, а такжеcom.sun.jmx.remote.util.OrderClassLoader
S, а также массивыHashtable$Entry
(примерно столько же каждая; около половины как у хехмамок и векторов)
Существуют некоторые выдержки из вывода GC ниже; Моя интерпретация из них кажется, что CMS GC становится прерванным, не отказуясь до Stop-The World GC. Я как-то ошибочно истолковывать этот выход? Есть ли что-то, что бы заставить это?
Во время обычного времени выполнения блоки выходных блоков CMS GC выглядят так:
36301.827: [GC [1 CMS-initial-mark: 1856321K(2621330K)] 1879456K(3093312K), 1.7634200 secs] [Times: user=0.17 sys=0.00, real=0.18 secs]
36303.638: [CMS-concurrent-mark-start]
36314.903: [CMS-concurrent-mark: 7.804/11.264 secs] [Times: user=2.13 sys=0.06, real=1.13 secs]
36314.903: [CMS-concurrent-preclean-start]
36314.963: [CMS-concurrent-preclean: 0.037/0.060 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
36314.963: [CMS-concurrent-abortable-preclean-start]
36315.195: [GC 36315.195: [ParNew: 428092K->40832K(471872K), 1.1705760 secs] 2284414K->1897153K(3093312K), 1.1710560 secs] [Times: user=0.13 sys=0.02, real=0.12 secs]
CMS: abort preclean due to time 36320.059: [CMS-concurrent-abortable-preclean: 0.844/5.095 secs] [Times: user=0.74 sys=0.05, real=0.51 secs]
36320.062: [GC[YG occupancy: 146166 K (471872 K)]36320.062: [Rescan (parallel), 1.54078550 secs]36321.603: [weak refs processing, 0.0042640 secs] [1 CMS-remark: 1856321K(2621440K)] 2002488K(3093312K), 1.5456150 secs] [Times: user=0.18 sys=0.03, real=0.15 secs]
36321.608: [CMS-concurrent-sweep-start]
36324.650: [CMS-concurrent-sweep: 2.686/3.042 secs] [Times: uesr=0.66 sys=0.02, real=0.30 secs]
36324.651: [CMS-concurrent-reset-start]
36324.700: [CMS-concurrent-reset: 0.050/0.050 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
вот и все; Следующая строка будет следующим gc parnew.
Когда мы заставляем GC с помощью JMAP -Histo: Live, мы вместо этого получаем:
48004.088: [CMS-concurrent-mark: 8.012/8.647 secs] [Times: user=1.15 sys=0.02, real=0.87 secs]
(concurrent mode interrupted)
Содержит ~ 125 строк формы ниже: (некоторые генерируемыеMethodaccessor, некоторые генерируемые целесообразныеСтроинизационные конструктивные ресурсы, некоторые генерируемыеConstructoraccessor и т. Д.
[Unloading class sun.reflect.GeneratedMethodAccessor3]
с последующим:
: 1911295K->562232K(2621440K), 15.6886180 secs] 2366440K->562232K(3093312K), [CMS Perm: 52729K->51864K(65536K)], 15.6892270 secs] [Times: user=1.55 sys=0.01, real=1.57 secs]
Заранее спасибо!
Решение
com.sun.jmx.remote.UTIL.OrderClassAssLoader Используется в удаленном слое для JMX и быстрый обзор кода предполагает, что они созданы как часть процесса Unmarshalling для удаленных запросов внутри JVM. Срок службы этих классов будет напрямую связан с жизнью того, что было неуместно, что было немархаллировано таким, что когда-то больше нет никаких ссылок на то, что может быть выпущена классная загрузка.
Я бы не был удивлен, если в этом случае наличие этих экземпляров было прямым результатом того, что вы используете JConsole, чтобы изучить на работу в JVM. И похоже, что они просто будут очищены GC как часть нормальной работы.
Я думаю, возможно, есть ошибка в реализации JMX (кажется, вряд ли в сравнительно актуальном JVM) или, возможно, у вас есть некоторые пользовательские MBeans или используют некоторые пользовательские инструменты JMX, которые вызывают проблему. Но в конечном итоге я подозреваю, что загрузка, вероятно, является красной сельди, и проблема лежит в другом месте (сломанный GC или какая-то другая утечка).
Другие советы
Технические примечания: Мы запускаем Sun JDK 1.6.0_07, 32-битный, на коробке Linux 2.6.9. Обновление версии JDK не является обязательным вариантом, если нет неизбежной, основной причины.
Несколько новых версий Java имели обновления в CMS Collector. Примечательно 6U12, 6U14 и 6U18.
Я не эксперт с учетом GC, но я предполагаю, что предсказанные исправления в 6U14 мая исправить проблему, которую вы видите. Конечно, я мог бы сказать то же самое около 6U18 классовых разгрузочных ошибок. Как я уже сказал, я не эксперт в GC Parts.
Есть исправления для:
- 6U10: (влияет на 6U4 +) CMS никогда не очищает референов, когда -xx: + parallelrefprocenabled
- 6U12: CMS: Неправильное кодирование массивов преодоленных объектов во время одновременного предсплата
- 6U12: CMS: Неправильная обработка переполнения при использовании параллельной параллельной маркировки
- 6U14: CMS: Отказ утверждения «IS_CMS_THREAD == Тема :: Текущий () -> IS_CONCURRENTGC_THREAD ()»
- 6U14: CMS: нужна CMSInitiatiTingPermoccupancanceFraction для Перми, разводки от CMSInitiatiTingoccupancyFraction
- 6U14: CMS Assert: _Concurrent_Iterater_safe_limit обновление пропущено
- 6U14: CMS: Неправильная обработка переполнения во время предслащивания списков контроля
- 6U14: SIGSEGV или (! IS_NULL (V), «Значение OOP никогда не может быть нулевым») Утверждение при работе с CMS и Coops
- 6U14: CMS: Livelock в CompactibleFreelistsPace :: Block_size ().
- 6U14: заставить CMS работать с сжатым упс
- 6U18: CMS: Dump Core с -xx: + USECompressedoops
- 6U18: CMS: Ошибки, связанные с разгрузкой класса
- 6U18: CMS: undedInitialcardmarks небезопасно в присутствии предсредственного положения CMS
- 6U18: [регрессия] -xx: newratio с -xx: + UseconcmarksweepGC вызывает фатальную ошибку
- 6U20: Марки карты могут быть отложены слишком длинные
В дополнение к всем вышеперечисленным, 6U14 также представил Г1. Сборщик мусора, хотя он все еще в тестировании. G1 предназначен для замены CMS в Java 7.
G1 может использоваться в Java 6U14 и новее со следующими коммутаторами командной строки:
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC
Я бы начал с чего-то гораздо проще, как:
-server -Xms3072m -Xmx3072m -XX:+UseParallelOldGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
И посмотрите, что это соответствует вашим потребностям.
Похоже, вы строите объекты, которые указывают на своих владельцев (точки в B указывает на а). Это приводит к возмещению ссылок, оставшиеся более ноль, поэтому сборщик мусора не может их очистить. Вам нужно сломать цикл, когда вы отпускаете их. Nullificing ссылка на либо B, либо B решит проблему. Это работает даже в более крупной ссылке выглядит как (A -> B -> C -> D -> A). Векторы и объектные массивы могут использоваться вашими хэсмапами.
Прерывирование удаленных погрузчиков может указывать на невозможность очистки и тесных ссылок на объекты, загруженные через JNDI или другой метод удаленного доступа.
Редактировать: Я занял второй взгляд на вашу последнюю строку. Вы можете увеличить выделение Пермь.