Как контролировать использование памяти Java?

У нас есть приложение 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». В основном его аргументы были такими же, какобсуждать здесь. But I still don't understand:

how can I have my memory usage curve? what is wrong with the explicit gc()? I don't care about small performance issues which can happen with explicit gc() and which I would estimate in 1-3%. What I need is memory and thread monitor which helps me in analysis of our system on customer site.

Ответы на вопрос(13)

Решение Вопроса

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

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

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

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

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

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

Пример: JVM Memory Dashboard

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

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

Если вы используете предоставленную 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.

Посмотрите на то, что происходит внутри Tomcat через Visual VM. http://www.skill-guru.com/blog/2010/10/05/increasing-permgen-size-in-your-server/ alt text

alt text

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

XX:-PrintGC Print messages at garbage collection. Manageable.

-XX:-PrintGCDetails Print more details at garbage collection. Manageable. (Introduced in 1.4.0.)

-XX:-PrintGCTimeStamps Print timestamps at garbage collection. Manageable (Introduced in 1.4.0.)

-XX:-PrintTenuringDistribution Print tenuring age information.

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

Если вы используете Java 1.5, вы можете посмотреть на ManagementFactory.getMemoryMXBean (), который дает вам цифры на все виды памяти. куча и не куча, Пермь-генерал.

Хороший пример можно найти там http://www.freshblurbs.com/explaining-java-lang-outofmemoryerror-permgen-space

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

In theory, theory and practice are the same. In practice, they are not.

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

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

Ваш ответ на вопрос