Java ConcurrentMarkSweep сборщик мусора не удаляет весь мусор - PullRequest
10 голосов
/ 06 октября 2010

Краткая форма: сборщик мусора CMS, по-видимому, не может собирать постоянно увеличивающееся количество мусора; в итоге наша JVM заполняется, и приложение перестает отвечать на запросы. Принудительный сбор GC с помощью внешнего инструмента (JConsole или jmap -histo:live) очищает его один раз.

ОБНОВЛЕНИЕ: проблема связана с плагином JTop для JConsole; если мы не запустим JConsole или не запустим его без плагина JTop, поведение исчезнет.

(Технические примечания: мы запускаем 32-разрядную версию Sun JDK 1.6.0_07 на платформе 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 все больше и больше памяти все еще используется. Через несколько часов система переходит в устойчивое состояние, когда в CMS старого поколения примерно 2 ГБ используемой памяти.

Звучит как классическая утечка памяти, за исключением того, что если мы используем какой-либо инструмент, который заставляет полный GC (нажать кнопку «собрать мусор» в JConsole или запустить jmap -histo:live и т. Д.), Старый ген внезапно падает до ~ 500 МБ, и наше приложение снова становится отзывчивым в течение следующих нескольких часов (в течение этого времени продолжается та же самая схема - после каждого полного GC все больше и больше старого поколения будет заполнено).

Одна вещь, на которую следует обратить внимание: в JConsole указанный счетчик GC ConcurrentMarkSweep останется равным 0, пока мы не принудительно настроим GC с помощью jconsole / jmap / и т. Д.

Используя jmap -histo и jmap -histo:live последовательно, я могу определить, что явно не собранные объекты состоят из:

  • несколько миллионов HashMap с и массивы HashMap$Entry (в соотношении 1: 1)
  • несколько миллионов Vector с и массивов объектов (соотношение 1: 1 и примерно столько же, сколько число хэш-карт)
  • несколько миллионов HashSet, Hashtable и com.sun.jmx.remote.util.OrderClassLoader с, а также массивы Hashtable$Entry (примерно столько же, сколько у каждого; примерно вдвое меньше, чем хэш-карты и векторы)

Ниже приведены некоторые выдержки из вывода ГХ; моя интерпретация, по-видимому, заключается в том, что CMS GC прерывается, не переходя к 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]

и все; следующая строка будет следующей 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 строк в форме ниже: (некоторые GeneratedMethodAccessor, некоторые GeneratedSerializationConstructorAccessor, некоторые GeneratedConstructorAccessor и т. Д.)

[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]

Заранее спасибо!

Ответы [ 4 ]

7 голосов
/ 06 октября 2010

com.sun.jmx.remote.util.OrderClassLoader используется в слое удаленного доступа для JMX, и быстрый обзор кода показывает, что они созданы как часть процесса демаршаллинга для удаленных запросов внутри JVM.Время жизни этих загрузчиков классов будет напрямую связано с временем жизни того, что было распаковано, так что, если больше нет ссылок на эту вещь, загрузчик классов может быть освобожден.

Я не удивлюсь, если вВ этом случае наличие этих экземпляров было прямым результатом того, что вы использовали JConsole для проверки того, что происходит в JVM.И похоже, что они просто будут очищены GC как часть нормальной работы.

Я полагаю, возможно, есть ошибка в реализации JMX (кажется маловероятной в относительно современной JVM) иливозможно, у вас есть несколько пользовательских MBean-компонентов или вы используете некоторые пользовательские инструменты JMX, которые вызывают проблему.Но, в конечном счете, я подозреваю, что OrderClassLoader, вероятно, является «красной селедкой», и проблема кроется в другом месте (сломанный GC или какая-то другая утечка).

5 голосов
/ 06 октября 2010

Технические примечания: у нас работает Sun JDK 1.6.0_07, 32-битная, на Linux 2.6.9. Обновление версии JDK не действительно вариант, если нет неизбежная основная причина.

Несколько новых версий Java были обновлены для сборщика мусора CMS. В частности, 6u12, 6u14 и 6u18.

Я не эксперт по GC, но я предполагаю, что предварительные исправления в 6u14 могут решить проблему, которую вы видите. Конечно, я мог бы сказать то же самое об ошибках разгрузки класса 6u18. Как я уже сказал, я не эксперт в GC.

Есть исправления для:

  • 6u10: (влияет на 6u4 +) CMS никогда не очищает ссылки, когда -XX: + ParallelRefProcEnabled
  • 6u12: CMS: неправильное кодирование массивов переполненных объектов при одновременной предварительной очистке
  • 6u12: CMS: неправильная обработка переполнения при использовании параллельной параллельной маркировки
  • 6u14: CMS: ошибка подтверждения "is_cms_thread == Thread :: current () -> is_ConcurrentGC_thread ()"
  • 6u14: CMS: требуется CMSInitiatingPermOccupancyFraction для Перми, в отличие от CMSInitiatingOccupancyFraction
  • 6u14: CMS assert: _concurrent_iteration_safe_limit обновление пропущено
  • 6u14: CMS: неправильная обработка переполнения во время предварительной очистки списков литературы
  • 6u14: утверждение SIGSEGV или (! Is_null (v), «значение oop никогда не может быть нулевым») при работе с CMS и COOP
  • 6u14: CMS: Livelock в CompactibleFreeListSpace :: block_size ().
  • 6u14: заставить CMS работать со сжатыми упами
  • 6u18: CMS: дамп ядра с -XX: + UseCompressedOops
  • 6u18: CMS: ошибки, связанные с выгрузкой классов
  • 6u18: CMS: ReduceInitialCardMarks небезопасны при наличии предварительной очистки cms
  • 6u18: [Регрессия] -XX: NewRatio с -XX: + UseConcMarkSweepGC вызывает фатальную ошибку
  • 6u20: метки карты могут быть отложены слишком долго

Помимо всего вышеперечисленного, 6u14 также представил сборщик мусора G1 , хотя он все еще находится в стадии тестирования. G1 предназначен для замены CMS в Java 7.

G1 может использоваться в Java 6u14 и новее со следующими параметрами командной строки:

-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC

0 голосов
/ 07 октября 2010

Я бы начал с чего-то гораздо более простого, например:

-server -Xms3072m -Xmx3072m -XX:+UseParallelOldGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 

и посмотрим, отвечает ли это вашим потребностям.

0 голосов
/ 06 октября 2010

Похоже, что вы строите объекты, которые указывают на их владельцев (A указывает на B указывает на A).Это приводит к тому, что число ссылок остается больше нуля, поэтому сборщик мусора не может их очистить.Вам нужно разорвать цикл, когда вы отпустите их.Обнуление ссылки в A или B решит проблему.Это работает даже в увеличенном виде выглядит (A -> B -> C -> D -> A).Векторы и массивы объектов могут использоваться вашими HashMaps.

Наличие удаленных загрузчиков может указывать на сбой очистки и закрытия ссылок на объекты, загруженные через JNDI или другой метод удаленного доступа.

РЕДАКТИРОВАТЬ: я взглянул на вашу последнюю строку.Вы можете увеличить распределение разрешений.

...