2009-06-29 2 views
50

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

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

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

alt text

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

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

монитор с jvisualvm - дает лучшую статистику. –

ответ

47

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

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

+1

Просто смотрел на VisualVM - это мило. –

+4

System.gc() может нанести вред производительности, поскольку он мешает алгоритмам GC. Это может ухудшить все последующие GC. –

+2

[править] Упование продемонстрировать ваши претензии? Пожалуйста, объясните, как сделать вызов gc() «интерферировать» с помощью алгоритмов сбора мусора и как вызов метода «ухудшает» последующие вызовы? – amischiefr

9

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

Теоретически теория и практика одинаковы. На практике это не так.

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

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

+0

знаете ли вы, что это поведение просто порождает поток и запускается сразу же? применимо к текущей (Sun/Oracle/Hotspot) JVM (8)? – Ben

+1

Running System.gc часто, чтобы получить разумную статистику использования памяти, * будет * ухудшать производительность. Так что это не «просто сделай это». System.gc() разумна в таких случаях, как «ОК, теперь большая работа, требующая большой памяти, выполнена, и сейчас мне нечего делать, поэтому я собираю мусор, чтобы позже улучшить производительность небольших сборщиков мусора». –

5

Взгляните на аргументах 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 с чтением всего и всего, пишет Brian Goetz.

28

Есть инструменты, которые позволяют контролировать использование памяти ВМ. VM can expose memory statistics using JMX. Вы также можете указать print GC statistics, чтобы узнать, как память работает с течением времени.

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

+2

+1 за отличный комментарий о преждевременном продвижении по службе. –

5

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

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

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

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

+0

Как это смягчается, не вызывая System.gc()? В конце концов мусор JVM собирает, когда он работает на низкой памяти, так что тогда не произойдет задержка? (По крайней мере, это был мой опыт). – Yishai

+0

JVM позволяет вам указывать несколько алгоритмов GC. Они все делают какой-то инкрементный GC и могут иногда запускать полный GC (я не слишком подробно разбираюсь, но в основном только тогда, когда это необходимо - вроде как «последнее средство», например, когда нет достаточно места для создания новой копии некоторого раздела mem во время инкрементального gc). Если вы регулярно звоните в System.gc, это произойдет гораздо чаще. Вы по существу создаете собственную стратегию GC, которая «просто периодически запускает полный GC». У JVM есть лучшие стратегии, чем это. –

0

Как было предложено, попробуйте VisualVM, чтобы получить базовый вид.

Вы также можете использовать Eclipse MAT для более детального анализа памяти.

Это нормально, если вы используете System.gc(), если вы не зависите от него, для правильности вашей программы.

4

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

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

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

0

Проблема с system.gc заключается в том, что JVM уже автоматически выделяет время сборщику мусора на основе использования памяти.

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

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

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

2

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

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

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

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

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

9

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

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

Пример: JVM Memory Dashboard

Посмотрите на project website, чтобы увидеть скриншоты, описание функций и документацию.

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

+1

спасибо, я посмотрю. –

0

Добавления в список ответов, JConsole (входят в JDK каталоге/бен) является также хорошим интерфейсом, чтобы попробовать.

Reference