Как контролировать использование памяти Java? - PullRequest
57 голосов
/ 29 июня 2009

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

    System.gc();
    Runtime rt = Runtime.getRuntime();
    long usedMB = (rt.totalMemory() - rt.freeMemory()) / 1024 / 1024;
    logger.information(this, "memory usage" + usedMB);

Этот код работает нормально. Это означает, что он показывает кривую памяти, которая соответствует действительности. Когда мы создаем большой файл XML из БД, кривая идет вверх, а после завершения извлечения она идет вниз.

alt text

Консультант сказал нам, что явно вызывать gc () неправильно, «пусть jvm решает, когда запускать gc». В основном его аргументы были такими же, как здесь обсуждалось . Но я все еще не понимаю:

  • как мне получить кривую использования памяти?
  • что не так с явным gc ()? Меня не волнуют небольшие проблемы с производительностью, которые могут возникнуть с явным gc () и которые я бы оценил в 1-3%. Мне нужен монитор памяти и потоков, который помогает мне анализировать нашу систему на сайте заказчика.

Ответы [ 13 ]

51 голосов
/ 29 июня 2009

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

Нет ничего действительно "неправильного" с явными вызовами gc (). Однако помните, что когда вы вызываете gc (), вы «предлагаете» запустить сборщик мусора. Нет никаких гарантий, что он будет запущен в то время, когда вы запускаете эту команду.

28 голосов
/ 29 июня 2009

Существуют инструменты, позволяющие контролировать использование памяти виртуальной машиной. Виртуальная машина может предоставлять статистику памяти, используя JMX . Вы также можете распечатать статистику ГХ , чтобы увидеть, как память работает с течением времени.

Invoking System.gc () может нанести вред производительности GC, поскольку объекты будут преждевременно перемещены из нового в старое поколение, а слабые ссылки будут преждевременно удалены. Это может привести к снижению эффективности памяти, увеличению времени GC и уменьшению обращений к кешу (для кешей, использующих слабые ссылки). Я согласен с вашим консультантом: System.gc () - это плохо. Я бы зашел так далеко, что отключил с помощью переключателя командной строки.

10 голосов
/ 03 июля 2014

Вы можете взглянуть на stagemonitor . Это монитор производительности Java-приложений с открытым исходным кодом. Он фиксирует метрики времени ответа, метрики JVM, детали запроса (включая стек вызовов, захваченный профилировщиком запросов) и многое другое. Накладные расходы очень низкие.

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

Пример: JVM Memory Dashboard

Загляните на веб-сайт проекта , чтобы увидеть скриншоты, описания функций и документацию.

Примечание: я разработчик stagemonitor

9 голосов
/ 29 июня 2009

Я бы сказал, что консультант прав в теории, а вы правы на практике. Как говорится :

Теоретически, теория и практика совпадают. На практике их нет.

В спецификации Java сказано, что System.gc предлагает вызывать сборщик мусора. На практике он просто порождает поток и сразу запускается на Sun JVM.

Хотя теоретически вы можете испортить какую-то тонко настроенную реализацию сборки мусора на JVM, если вы не пишете универсальный код, предназначенный для развертывания на любой JVM, не беспокойтесь об этом. Если это работает для вас, сделайте это.

8 голосов
/ 29 июня 2009

Вы пробовали JMX?

http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html

alt text
(источник: sun.com )

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

Посмотрите на то, что происходит внутри Tomcat через Visual VM. http://www.skill -guru.com / блог / 2010/10 / 05 / увеличение-PermGen размера в Вашем сервере / alt text

alt text

5 голосов
/ 29 июня 2009

Явное выполнение System.gc () в производственной системе - ужасная идея. Если объем памяти достигнет какого-либо размера, вся система может зависнуть, пока работает полный сборщик мусора. На сервере размером в несколько гигабайт это может быть очень заметно, в зависимости от того, как настроен jvm, сколько у него запаса, и т. Д. И т. Д. - я видел паузы длительностью более 30 секунд.

Другая проблема заключается в том, что, явно вызывая GC, вы на самом деле не отслеживаете, как JVM выполняет GC, вы фактически изменяете его - в зависимости от того, как вы настроили JVM, он будет собирать мусор, когда это необходимо и обычно постепенно (он не просто запускает полный сборщик мусора, когда ему не хватает памяти). То, что вы будете распечатывать, не будет похоже на то, что JVM будет делать самостоятельно - во-первых, вы, вероятно, увидите меньше автоматических / инкрементальных сборщиков мусора, поскольку вы будете очищать память вручную.

Как отмечается в сообщении Ника Холта, варианты печати активности GC уже существуют в виде флагов JVM.

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

5 голосов
/ 29 июня 2009

Посмотрите на аргументы JVM: http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp#DebuggingOptions

XX: -PrintGC Печатать сообщения при сборке мусора. Управляемость.

-XX: -PrintGCDetails Печатать больше деталей при сборке мусора. Управляемость. (Введено в 1.4.0.)

-XX: -PrintGCTimeStamps Печатать метки времени при сборке мусора. Управляемый (введено в 1.4.0.)

-XX: -PrintTenuringDistribution Распечатать информацию о возрасте владения.

Хотя вы не собираетесь расстраивать JVM явными вызовами System.gc(), они могут не дать ожидаемого эффекта. Чтобы действительно понять, что происходит с памятью в JVM с чтением чего угодно и всего, что пишет Брайан Гетц .

4 голосов
/ 30 июня 2009

Если вам нравится хороший способ сделать это из командной строки, используйте jstat:

http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jstat.html

Он дает необработанную информацию через настраиваемые интервалы, что очень полезно для целей ведения журнала и построения графиков.

2 голосов
/ 15 августа 2012

Если вы используете предоставленную JMX историю запусков GC, вы можете использовать то же самое до / после чисел, вам просто не нужно форсировать GC.

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

Например, на виртуальной машине Oracle HotSpot с ParNewGC, есть JMX MBean с именем java.lang:type=GarbageCollector,name=PS Scavenge, он имеет атрибут LastGCInfo, он возвращает CompositeData последнего запуска мусорщика YG. Записывается с duration, абсолютными startTime и memoryUsageBefore и memoryUsageAfter.

Просто используйте таймер, чтобы прочитать этот атрибут. Всякий раз, когда появляется новый startTime, вы знаете, что он описывает новое событие GC, вы извлекаете информацию о памяти и продолжаете опрос для следующего обновления. (Не уверен, что можно использовать AttributeChangeNotification.)

Совет: в вашем таймере вы можете измерить расстояние до последнего прогона ГХ, и если оно слишком велико для возобновления вашего графика, вы можете вызвать System.gc () условно. Но я бы не стал этого делать в экземпляре OLTP.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...