Подтвердить что ты не робот

Как убедить Java Garbage Collector запускать, когда рабочий набор невелик?

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

Это серверное приложение, которое обычно имеет около 5M живых объектов. Каждые 5 минут мы выполняем задачу анализа, которая занимает ~ 60 секунд. Если во время анализа запускается полный GC, то будет находиться около 40M живых объектов. Дополнительные объекты 35M становятся мусором, когда анализ завершается. Сервер должен всегда реагировать на запросы (даже во время анализа).

Мы обнаружили, что полный GC занимает около 1,5 секунд, если он вызван, когда анализ не работает, но около 15 секунд во время анализа. К сожалению, наш шаблон распределения таков, что полные GC обычно возникают во время анализа, хотя анализ выполняется только в 20% случаев. (Каждый третий или четвертый анализ запуска запускает полный GC.)

Я добавил код, чтобы вызвать сильно прерванный System.gc() перед началом анализа, если свободное пространство в старом поколении ниже определенного порога (5 ГБ). Преимущество было очень существенным: мы получаем 1,5 секунды паузы вместо 15 секунд паузы, и мы освобождаем больше мусора в сделку. Однако иногда вызов System.gc() игнорируется, и через несколько минут мы завершаем 15-секундную паузу, когда GC запускается автоматически.

Мой вопрос, тогда: есть ли что-то, что мы можем сделать, чтобы сильнее убедить сборщика мусора работать? Мы запускаем 1.7.0_09-icedtea и используем Parallel GC. Я хотел бы либо (a) надежный способ вручную принудительно собрать мусор, либо (b) каким-то образом настроить коллектор, чтобы он сделал более интеллектуальное автоматическое решение. (б) кажется трудным, так как мне не ясно, как коллекционер мог обнаружить, что наш рабочий набор меняется в этом драматическом стиле.

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

добавление. В производстве наш опыт показывает, что System.gc() обычно запускает полную сборку мусора; по крайней мере, в ситуациях, когда мы это называем. (Мы называем это только раз в 10-30 минут, а куча несколько, но не полностью заполнена мусором.) Было бы неплохо иметь возможность более надежно запускать сборку мусора, но это помогает нам большую часть времени.

4b9b3361

Ответ 1

Ваша проблема в том, что вы используете два приложения с совершенно разными требованиями и профилями памяти в одной JVM.

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

Ответ 2

Рассмотрите возможность использования не управляемой памяти, т.е. ByteBuffer вместо массивов байтов.

Я могу предложить только хак, который будет нуждаться в некоторой настройке, а затем может работать или не работать. Сначала я попробую более разумные решения. Когда вы хотите заставить GC, сделайте это, выделив много памяти. Сделайте это так, чтобы память могла быть немедленно восстановлена, но чтобы все выделение не могло быть оптимизировано (что-то вроде sum += new byte[123456].hashCode() должно делать). Вам нужно найти надежный метод определения момента остановки. Объект с финализатором может рассказать вам или, возможно, смотреть runtime.getFreeMemory может помочь.

Ответ 3

Вместо того, чтобы отвечать на ваш вопрос напрямую (я не могу), я хотел бы предложить возможную альтернативу.

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

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

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

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

Ответ 4

Я обнаружил, что java GC очень плохо работает с большим количеством объектов (объекты 20-100 м). ваша ситуация была бы хуже, если бы эти объекты на самом деле оставались живыми, потому что GC был бы ужасен, даже если бы не было ничего, что можно было бы собрать.

решение состоит в том, чтобы уменьшить количество объектов (а не общую память, которую вы используете). Я бы осмелился предположить, что на этапе анализа используются коллекции и множество примитивных оберток (Integer, Long и т.д.). Если это так, одним из решений является переход на примитивную библиотеку коллекций. одна из таких библиотек - это то, что я создал для решения аналогичной проблемы, с которой я столкнулся, когда я долгое время запускал симуляцию с живыми объектами длиной 100 м. Эта библиотека называется Banana, подробности см. В wiki.