У меня есть приложение, которое использует слишком много агентов: 25,000, чтобы быть точным. Это использует Scala 2.7.7 и работает на jdk6_u18. Это в основном слушает и обрабатывает данные рынка и имеет очень мало состояния.
Это запускается в 8:02 каждый день, и в течение часа это отказало с OutOfMemoryError
. "Ага" Вы говорите, "у Вас есть утечка памяти!" За исключением того, что, когда я перезапускаю его, это никогда не отказывает снова для остальной части дня! Это несмотря на то, чтобы расти и GC и ЦП наверху, когда американские рынки открываются в 14:30.
Несколько анекдотических результатов:
verbose:gc
включенныйНесколько вопросов представляют себя:
Я теперь пробую G1
видеть, имеет ли это какое-либо значение. Завтра я обновлю этот вопрос!
Я думаю, что просто застал его на месте:
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]
Это прекрасно снова! *Вздох*
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 получение себя в смертельную спираль в случаях с высокой пропускной способностью, где это продвинуло объекты в штатное поколение.
Есть ли у вас основания ожидать, что ваша куча будет медленно увеличиваться в размерах? Похоже, что по обоим следам он растет. Одна вещь, которую я делал много раз, - уменьшал свою кучу, чтобы попытаться усугубить проблему. Однако 256M - это примерно нижний предел для Scala.
Одна вещь, которую я заметил раньше, это то, что если у вас есть недолговечные объекты, которые выходят из поколения Эдема из-за слишком большого давления, они могут постепенно вас убить. Это может произойти, когда есть всплеск активности (может быть, у вас всплеск утром?), И ваше райское пространство недостаточно велико. Scala в целом и актеры в частности интенсивно используют небольшие недолговечные объекты, и кажется, что есть волшебный порог, который, когда вы пересекаете его, все идет вниз по склону. Так что один запуск будет нормальным, а следующий вылетит и сгорит.
Еще я заметил ранее, что настройки GC, которые хорошо работают в OSX / x86, часто не работают в Sparc / Solaris и наоборот. Если вы используете CoolThreads, я предлагаю вам настроить сборщик мусора так, чтобы он имел по одному потоку сборщика мусора на поток в вашем планировщике / пуле.
Это поднимает еще один вопрос - убедитесь, что планировщик не создает новые потоки волей-неволей. Иногда так и будет. Я бы сказал, что вам почти всегда следует вручную устанавливать ограничение на потоки.Я не знаю, насколько это актуально, но интересным фактом о планировщике fork-join, который акторы используют по умолчанию, является то, что он предназначен для коротких задач, связанных с процессором. Выполнение операций ввода-вывода в потоках, которыми он управляет, приводит к ошибкам в своих предположениях. Конечно, все равно должно работать, но ...
Удачи! Я потерял много-много дней своей жизни из-за таких проблем.
Взгляните на некоторые из вариантов здесь: http://java.sun.com/performance/reference/whitepapers/tuning.html
Похоже, вы используете параллельную отметку-развертку коллекционер. Попробуйте установить:
-XX:+UseParallelGC
В данный момент я задаюсь вопросом, стоит ли пытаться заменить ваших актеров на Lift's или Akka? Хотя я думаю, что вряд ли это будет жучок с ними, они могут и не пощекотать, что бы это ни было причиной крушения.