Странное поведение GC приложения агентов Scala

У меня есть приложение, которое использует слишком много агентов: 25,000, чтобы быть точным. Это использует Scala 2.7.7 и работает на jdk6_u18. Это в основном слушает и обрабатывает данные рынка и имеет очень мало состояния.

Это запускается в 8:02 каждый день, и в течение часа это отказало с OutOfMemoryError. "Ага" Вы говорите, "у Вас есть утечка памяти!" За исключением того, что, когда я перезапускаю его, это никогда не отказывает снова для остальной части дня! Это несмотря на то, чтобы расти и GC и ЦП наверху, когда американские рынки открываются в 14:30.

Несколько анекдотических результатов:

  • это работает на Солярисе. Когда я раньше выполнял его на Linux, это никогда не отказывало вообще.
  • Я попытался смешать с калибровкой "кучи" поколения, выделив больше памяти и т.д. Я думаю, что Это не имеет никакого значения
  • Поведение коллектора, кажется, отличается, когда я имею verbose:gc включенный

Несколько вопросов представляют себя:

  1. Почему был бы поведение этой программы отличаться между Linux и Солярисом?
  2. Почему поведение отличалось бы между запуском его в 8,02 и запуском его в 8,42?
  3. Я услышал, что библиотека агентов имела некоторые проблемы утечки памяти. Каковы они были, когда они были зафиксированы и как я обнаружу, могло ли что-либо "подобное" продолжаться здесь? (Вещи искать в jhat и т.д.)
  4. У кого-либо есть идея относительно того, что могло бы продолжаться?

Я теперь пробую G1 видеть, имеет ли это какое-либо значение. Завтра я обновлю этот вопрос!

Некоторый вывод от G1 с verbose:gc на

Я думаю, что просто застал его на месте:

600.290: [Полный GC 255M-> 144M (256M), 1.5772616 secs]
602.084: [Пауза GC (молодой) 227M-> 145M (256M), 0.0556769 secs]
602.418: [Полный GC 254M-> 144M (256M), 1.6415216 secs]
604.279: [Пауза GC (молодой) 227M-> 145M (256M), 0.0415157 secs]
604.602: [Полный GC 255M-> 145M (256M), 1.6041762 secs]
606.422: [Пауза GC (молодой) 227M-> 145M (256M), 0.0237441 secs]
606.710: [Полный GC 254M-> 145M (256M), 1.6022185 secs]

И затем немного позже (Вы видите, что полный GC занял больше времени и исправляет меньше),

849.084: [Полный GC 254M-> 176M (256M), 1.9658754 secs]
851.191: [Пауза GC (молодой) 228M-> 176M (256M), 0.0218611 secs]
851.414: [Полный GC 254M-> 176M (256M), 1.9352357 secs]
853.492: [Пауза GC (молодой) 228M-> 176M (256M), 0.0224688 secs]
853.716: [Полный GC 254M-> 176M (256M), 1.9339705 secs]
855.793: [Пауза GC (молодой) 228M-> 176M (256M), 0.0215707 secs]
856.009: [Полный GC 254M-> 176M (256M), 1.9805797 secs]
858.137: [Пауза GC (молодой) 228M-> 176M (256M), 0.0223224 secs]

Некоторый вывод от G1 с verbose:gc прочь

Это прекрасно снова! *Вздох*

303.656: [Пауза GC (молодой) 225M-> 93M (256M), 0.1680767 secs]
308.060: [Пауза GC (молодой) 226M-> 94M (256M), 0.1793724 secs]
312.746: [Пауза GC (молодой) 227M-> 93M (256M), 0.1674851 secs]
316.162: [Пауза GC (молодой) 227M-> 95M (256M), 0.1826145 secs]
320.147: [Пауза GC (молодой) 226M-> 94M (256M), 0.1656664 secs]
325.978: [Пауза GC (молодой) 226M-> 93M (256M), 0.1475760 secs]
330.176: [Пауза GC (молодой) 226M-> 94M (256M), 0.1727795 secs]

и очень, намного позже это все еще в порядке!

25882.894: [Пауза GC (молодой) 224M-> 125M (256M), 0.2126515 secs]
25884.880: [Пауза GC (молодой) 224M-> 126M (256M), 0.2059802 secs]
25887.027: [Пауза GC (молодой) 224M-> 125M (256M), 0.1851359 secs]
25889.940: [Пауза GC (молодой) 223M-> 126M (256M), 0.2046496 secs]
25891.567: [Пауза GC (молодой) 224M-> 126M (256M), 0.1600574 secs]

и позже все еще, полный GC

37180.191: [Пауза GC (молодой) 225M-> 154M (256M), 0.1716404 secs]
37182.163: [Пауза GC (молодая) (начальная метка) 225M-> 153M (256M) 37182.326: [GC concurrent-mark-start], 0.1622246 secs]
37183.089: [Параллельный конец метки GC, 0,7635219 секунды]
37183.090: [Комментарий GC, 0.0032547 secs]
37183.093: [GC concurrent-count-start]
37183.297: [Параллельный конец количества GC, 0.2043307]
37183.393: [Очистка GC 198M-> 198M (256M), 0.0068127 secs]
37183.400: [GC concurrent-cleanup-start]
37183.400: [Параллельный конец очистки GC, 0.0000393]
37183.648: [Пауза GC (молодой) 222M-> 153M (256M), 0.1483041 secs]
37184.235: [Пауза GC (частичный) 171M-> 91M (256M), 0.2520714 secs]
37187.223: [Пауза GC (молодой) 221M-> 92M (256M), 0.1721220 secs]

ОБНОВЛЕНИЕ

Ну, начиная с переключения на сборщик "мусора" G1 на jdk1.6.0_18 приложение вело себя само в три дня подряд. Я подозреваю, что Erik корректен в его анализе ситуации о VM получение себя в смертельную спираль в случаях с высокой пропускной способностью, где это продвинуло объекты в штатное поколение.

9
задан 12 revs, 2 users 96% 23 May 2017 в 02:01
поделиться

2 ответа

Есть ли у вас основания ожидать, что ваша куча будет медленно увеличиваться в размерах? Похоже, что по обоим следам он растет. Одна вещь, которую я делал много раз, - уменьшал свою кучу, чтобы попытаться усугубить проблему. Однако 256M - это примерно нижний предел для Scala.

Одна вещь, которую я заметил раньше, это то, что если у вас есть недолговечные объекты, которые выходят из поколения Эдема из-за слишком большого давления, они могут постепенно вас убить. Это может произойти, когда есть всплеск активности (может быть, у вас всплеск утром?), И ваше райское пространство недостаточно велико. Scala в целом и актеры в частности интенсивно используют небольшие недолговечные объекты, и кажется, что есть волшебный порог, который, когда вы пересекаете его, все идет вниз по склону. Так что один запуск будет нормальным, а следующий вылетит и сгорит.

Еще я заметил ранее, что настройки GC, которые хорошо работают в OSX / x86, часто не работают в Sparc / Solaris и наоборот. Если вы используете CoolThreads, я предлагаю вам настроить сборщик мусора так, чтобы он имел по одному потоку сборщика мусора на поток в вашем планировщике / пуле.

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

Удачи! Я потерял много-много дней своей жизни из-за таких проблем.

Взгляните на некоторые из вариантов здесь: http://java.sun.com/performance/reference/whitepapers/tuning.html

Похоже, вы используете параллельную отметку-развертку коллекционер. Попробуйте установить:

-XX:+UseParallelGC
4
ответ дан 3 November 2019 в 07:13
поделиться

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

1
ответ дан 3 November 2019 в 07:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: