Пять секретов... контроля производительности Java, часть 2

Мониторинг Java-процесса с помощью встроенных в JDK профайлеров

Не всем известно, что в состав JDK входит полноценный профайлер JConsole, но в этой статье рассматриваются еще пять автономных утилит профилирования. Читатель узнает о простых (и в некоторых случаях экспериментальных) способах мониторинга Java™-процесса и аналитических инструментах, которые помогают выявлять узкие места с точки зрения производительности, такие как дефицит потоков, взаимоблокировки и утечки объектов.

Тед Ньюворд, директор, Neward & Associates

Тед Ньюворд (Ted Neward) является директором компании “Neward & Associates”. Он занимается консультированием, преподаванием и презентациями продуктов на основе Java, .NET, XML-сервисов и других платформ. В настоящее время он живет недалеко от Сиэттла, штат Вашингтон.



19.10.2012

Об этом цикле статей

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

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

В JDK есть много утилит на основе командной строки, которые можно использовать для контроля и управления быстродействием Java-приложений. Они полезны даже несмотря на то, что большинство из них помечены как «экспериментальные» и не сопровождаются технической поддержкой. Некоторые даже могут послужить материалом для создания специальных инструментов с использованием JVMTI или JDI (см. раздел Ресурсы).

1. jps (sun.tools.jps)

Развить навыки по этой теме

Этот материал — часть knowledge path для развития ваших навыков. Смотри:

Для многих инструментов на основе командной строки нужно определить Java-процесс, который требуется контролировать. В этом смысле они мало отличаются от аналогичных инструментов для контроля процессов операционной системы, которым тоже нужно указать рабочий процесс.

Идентификатор VMID не во всем аналогичен собственному идентификатору процесса операционной системы (pid) ― потому-то в JDK и появилась утилита jps.

Использование jps в Java-процессе

Как и у большинства инструментов, поставляемых с JDK, и всех инструментов, упомянутых в этой статье, исполняемый файл jps представляет собой тонкую оболочку Java-класса или набора классов, которые и выполняют основную работу. В Windows® инструменты ― это exe-файлы, использующие для непосредственного обращения к нужному классу API JNI Invocation; в UNIX® большинство инструментов ― это символические ссылки на сценарий оболочки, который запускает общий пусковой механизм Java с указанием имени нужного класса.

Чтобы использовать функциональность jps (или любого другого инструмента) изнутри Java-процесса — такого как Ant-сценарий, — достаточно просто вызывать класс main(), главный класс любого инструмента. Для удобства имя этого класса указано в скобках после имени каждого инструмента.

jps— имя которого отражает утилиту ps, присутствующую в большинстве UNIX-систем, — указывает JVMID действующего Java-приложения. Как следует из названия, jps возвращает VMID для всех обнаруживаемых Java-процессов, запущенных на данном компьютере. Если jps не находит Java-процесс, это не означает, что его нельзя подключить или исследовать. Просто он себя не афиширует.

Если Java-процесс найден, jps укажет командную строку, которая использовалась для его запуска. Такой способ дифференцирования Java-процессов имеет большое значение, потому что с точки зрения операционной системы все Java-программы ― это просто java. В большинстве случаев знать VMID очень важно.

Начало работы с профайлерами

Простейший способ начать работу с утилитами профилирования ― воспользоваться демонстрационной программой, такой как SwingSet2 из demo/jfc/SwingSet2. Это позволит избежать потенциальных проблем с процессами, запущенными в качестве background/daemon-процессов. Освоив инструмент и оценив связанные с ним накладные расходы, вы сможете попробовать его на своих реальных программах.

Загрузив демо-приложение, запустите jps и запомните возвращенный vmid. Для достижения наилучшего эффекта запускайте Java-программу с набором свойств -Dcom.sun.management.jmxremote. Иначе будут доступны не все данные, собранные перечисленными ниже инструментами.


2. jstat (sun.tools.jstat)

Утилиту jstat можно использовать для сбора самых разнообразных статистических данных. Статистика jstat сортируется в "свойства", указанные первым параметром командной строки. В JDK 1.6, чтобы увидеть список свойств, нужно запустить jstat с параметром -options. Некоторые свойства приведены в листинге 1.

Листинге 1. Свойства jstat
-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation

В документации утилиты в JDK (см. раздел Ресурсы) говорится, что именно показывает каждое из свойств из листинга 1, но большинство из них используется для сбора информации о работе сборщика мусора или его компонентов. Свойство -class показывает загруженные и выгруженные классы (что делает утилиту полезной для обнаружения утечек ClassLoader в пределах сервера приложений или вашего кода), а -compiler и -printcompilation предоставляют сведения о JIT-компиляторе Hotspot.

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

Пусть VMID процесса SwingSet2, запущенного несколько минут назад, ― 5756; тогда следующая команда укажет jstat, что нужно сделать 10 копий дампа gc через каждые 250 мс:

jstat -gc 5756 250 10

Имейте в виду, что Sun (теперь Oracle) оставила за собой право без уведомления изменять состав выходных данных свойств и даже сами свойства. Это издержки использования неподдерживаемых утилит. Подробная информация о каждом из столбцов выходных данных jstat приведена в Javadocs.


3. jstack (sun.tools.jstack)

Определение того, что происходит внутри vis-a-vis Java-процесса, исполнительных потоков, обычно представляет трудность для диагностики. Например, когда приложение внезапно прекращает обработку, очевидно, что ему чего-то не хватило, но просто взглянув на код, трудно сказать, где произошла нехватка и почему.

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

Запуск jstack с VMID желаемого процесса приведет к созданию дампа стека. В этом случае jstack работает как нажатие клавиш Ctrl-Break в окне консоли с исполняемой Java-программой или вызов Thread.getAllStackTraces() или Thread.dumpStack() для каждого из объектов Thread ВМ. jstack выводит и сведения о не-Java потоках, запущенных в ВМ, которые не всегда доступны как объекты Thread.

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


4. jmap (sun.tools.jmap)

Иногда проблема заключается в утечке объекта, такого как ArrayList (а он может содержать тысячи объектов), который просто не появляется, когда нужно. Другая, более общая проблема, ― разрастание кучи, которая, кажется, никогда не сокращается, несмотря на активный сбор мусора.

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

Как и все прочие описанные здесь утилиты, использовать jmap довольно легко. Достаточно указать jmap VMID Java-процесса, который нужно "заснять", и некоторые параметры для описания результирующего файла. Параметры, передаваемые jmap, состоят из имени файла дампа и его типа ― текстовый или двоичный. Двоичный файл полезнее, но только в сочетании с каким-нибудь инструментом индексации — вручную продираться сквозь несколько мегабайт текста, заполненных шестнадцатеричными числами, ― не лучший способ провести день.

Для получения более общего представления о куче Java можно воспользоваться параметром -histo утилиты jmap. В этом случае она выдает текстовую гистограмму объектов, весомо представленных в куче на текущий момент, которые отсортированы по общему количеству байтов, занятых объектами конкретного типа. Она также указывает общее количество экземпляров этих объектов, что позволяет выполнить некоторые простые расчеты и сделать предположение об относительных расходах на экземпляр.

К сожалению, утилита jmap не позволяет определять значения за период и максимальное значение, как jstat, но ее легко вставить в цикл jmap (или jmap.main()) в сценарии оболочки или в другом классе, чтобы делать снимки периодически. (На самом деле, это было бы хорошим дополнением к jmap в виде расширения самого исходного кода OpenJDK или другой утилиты.)


5. jhat (com.sun.tools.hat.Main)

Сбросив дамп кучи в двоичный файл, для его анализа можно использовать утилиту jhat. Она создает сервер HTTP/HTML, который можно исследовать в браузере, рассматривая застывшую во времени кучу, объект за объектом. Еще лучше как-то автоматизировать анализ всей этой массы данных. Для этого jhat поддерживает синтаксис OQL.

Например, OQL-запрос для вывода всех строк, содержащих более 100 символов, будет выглядеть так:

select s from java.lang.String s where s.count >= 100

Результаты отображаются в виде ссылок на полное содержимое объектов с полями, указывающими на другие объекты в качестве дополнительных ссылок, которые затем можно разыменовать. С помощью OQL-запросов можно вызвать и методы самих объектов, а также включать в запрос регулярные выражения и применять встроенные инструменты запросов. Один такой инструмент, функция referrers(), отображает все ссылки, указывающие на объект данного типа. Вот запрос для поиска всех ссылок на объекты File:

select referrers(f) from java.io.File f

Полный синтаксис OQL и его функции можно найти на странице OQL Help внутри среды браузера jhat. Сочетание jhat с OQL представляет собой мощный инструмент целенаправленного исследования ведущей себя неподобающим образом кучи.


Заключение

Расширения JDK для профилирования могут быть очень полезны, когда нужно присмотреться к тому, что происходит внутри Java-процесса. Все инструменты, представленные в этой статье, можно использовать самостоятельно из командной строки. Их также полезно объединять с JConsole или VisualVM. В то время как JConsole и VisualVM обеспечивают общее предоставление виртуальной машины Java, специализированные инструменты, такие как jstat и jmap, позволяют выполнить тонкую настройку в процессе исследования.

Далее в цикле Пять секретов…: создание сценариев.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=841501
ArticleTitle=Пять секретов... контроля производительности Java, часть 2
publish-date=10192012