Стоило посетить презентацию Стаффана Ларсена (Oracle Java Serviceability Architect) « Диагностика вашего приложения в JVM » (Hilton Plaza A / B) только для изучения нового инструмента командной строки jcmd, поставляемого с Oracle JVM 7. Остальные презентация была для меня «бонусом», что было приятно для последней сессии, которую я посетил в среду JavaOne 2012
Oracle HotSpot JDK предоставляет jcmd, инструмент командной строки, разработанный для обеспечения обратной совместимости и прямой адаптации для будущих версий Java. Он предназначен для поддержки новых инструментов и функций, которые поставляются с новыми SDK в стандартизированном подходе. На следующем снимке экрана показано, что он используется для большинства основных jps- подобных функций (Ларсен упоминал jps почти так же кратко, как я только что говорил, и называл jcmd «как jps, но более мощный»).
Как показано на рисунке выше, jcmd можно использовать как jps.
Ларсен показал некоторые удобные функции команды jcmd. У него было несколько небольших примеров приложений Java, которые помогли ему продемонстрировать jcmd. В моих целях я запускаю jconsole в одном терминале на моей машине, а затем запускаю команды jcmd для той JVM, в которой работает jconsole. На следующем снимке экрана показано, как базовый (без аргументов) вызов jcmd предоставляет информацию об этом процессе JConsole.
jcmd поддерживает выполнение процессов JVM либо по идентификатору процесса (pid), либо по имени процесса. На следующем снимке экрана показано, как запустить jcmd для процесса JConsole с этим именем и передать его, чтобы узнать, какие параметры можно запустить для этого конкретного процесса. Обратите внимание, что я безуспешно пытался запустить это с «dustin» (без существующего процесса), чтобы доказать, что jcmd действительно показывает опции, доступные для запуска процессов.
Функция, продемонстрированная на последнем снимке экрана, является одной из наиболее убедительных причин перехода от существующих инструментов командной строки, поставляемых с Oracle JDK, к jcmd. Это изображение показывает, как jcmd может предоставить список доступных параметров для каждого процесса, что обеспечивает максимальную гибкость в плане поддержки предыдущих версий или будущих версий Java, которые поддерживают разные / новые команды.
Подобно тому, как справка jcmd <pid> (или заменяет pid на имя процесса) перечисляет доступные операции, которые могут быть выполнены jcmd для определенного процесса JVM, этот же механизм справки может быть запущен для любой из этих перечисленных команд [с синтаксисом jcmd <pid> <command_name> help (или используйте имя процесса вместо pid)], хотя я не мог заставить это работать должным образом на моей машине Windows.
На следующем изображении показано, как на самом деле запустить эту команду для этого процесса JVM, а не просто попросить о помощи.
На двух снимках экрана, показанных выше, я запустил jcmd для pid вместо имени процесса, просто чтобы показать, что он работает как с идентификатором процесса, так и с именем. На следующем снимке экрана показано выполнение jcmd для процесса JVM для получения флагов виртуальной машины и параметров командной строки из процесса JVM (pid этого экземпляра процесса JConsole — 3556).
Выполнение команды Thread.print в jcmd для поддержки процесса JVM упрощает просмотр потоков целевой JVM. Следующий вывод генерируется при запуске jcmd JConsole Thread.print против моего запущенного процесса JConsole.
3556: 2012-10-04 23:39:36 Full thread dump Java HotSpot(TM) Client VM (23.2-b09 mixed mode, sharing): "TimerQueue" daemon prio=6 tid=0x024bf000 nid=0x1194 waiting on condition [0x069af000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x23cf2db0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043) at java.util.concurrent.DelayQueue.take(DelayQueue.java:209) at javax.swing.TimerQueue.run(TimerQueue.java:171) at java.lang.Thread.run(Thread.java:722) "DestroyJavaVM" prio=6 tid=0x024be400 nid=0x1460 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE "AWT-EventQueue-0" prio=6 tid=0x024bdc00 nid=0x169c waiting on condition [0x0525f000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x291a90b0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043) at java.awt.EventQueue.getNextEvent(EventQueue.java:521) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:213) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139) at java.awt.EventDispatchThread.run(EventDispatchThread.java:97) "Thread-2" prio=6 tid=0x024bd800 nid=0x4a8 in Object.wait() [0x04bef000] java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x2917ed80> (a java.io.PipedInputStream) at java.io.PipedInputStream.read(PipedInputStream.java:327) - locked <0x2917ed80> (a java.io.PipedInputStream) at java.io.PipedInputStream.read(PipedInputStream.java:378) - locked <0x2917ed80> (a java.io.PipedInputStream) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177) - locked <0x29184e28> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:154) at java.io.BufferedReader.readLine(BufferedReader.java:317) - locked <0x29184e28> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:382) at sun.tools.jconsole.OutputViewer$PipeListener.run(OutputViewer.java:109) "Thread-1" prio=6 tid=0x024bd000 nid=0x17dc in Object.wait() [0x047af000] java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x29184ee8> (a java.io.PipedInputStream) at java.io.PipedInputStream.read(PipedInputStream.java:327) - locked <0x29184ee8> (a java.io.PipedInputStream) at java.io.PipedInputStream.read(PipedInputStream.java:378) - locked <0x29184ee8> (a java.io.PipedInputStream) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177) - locked <0x2918af80> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:154) at java.io.BufferedReader.readLine(BufferedReader.java:317) - locked <0x2918af80> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:382) at sun.tools.jconsole.OutputViewer$PipeListener.run(OutputViewer.java:109) "AWT-Windows" daemon prio=6 tid=0x024bc800 nid=0x16e4 runnable [0x0491f000] java.lang.Thread.State: RUNNABLE at sun.awt.windows.WToolkit.eventLoop(Native Method) at sun.awt.windows.WToolkit.run(WToolkit.java:299) at java.lang.Thread.run(Thread.java:722) "AWT-Shutdown" prio=6 tid=0x024bc400 nid=0x157c in Object.wait() [0x04c6f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x2918b098> (a java.lang.Object) at java.lang.Object.wait(Object.java:503) at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:287) - locked <0x2918b098> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722) "Java2D Disposer" daemon prio=10 tid=0x024bbc00 nid=0x3b8 in Object.wait() [0x0482f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x2918b128> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) - locked <0x2918b128> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151) at sun.java2d.Disposer.run(Disposer.java:145) at java.lang.Thread.run(Thread.java:722) "Service Thread" daemon prio=6 tid=0x024bb800 nid=0x1260 runnable [0x00000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread0" daemon prio=10 tid=0x024c6400 nid=0x120c waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE "Attach Listener" daemon prio=10 tid=0x024bb000 nid=0x1278 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" daemon prio=10 tid=0x024bac00 nid=0xe3c runnable [0x00000000] java.lang.Thread.State: RUNNABLE "Finalizer" daemon prio=8 tid=0x024a9c00 nid=0x15c4 in Object.wait() [0x046df000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x2918b358> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) - locked <0x2918b358> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177) "Reference Handler" daemon prio=10 tid=0x024a4c00 nid=0xe40 in Object.wait() [0x0475f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x2917e9c0> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:503) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133) - locked <0x2917e9c0> (a java.lang.ref.Reference$Lock) "VM Thread" prio=10 tid=0x024a3800 nid=0x164c runnable "VM Periodic Task Thread" prio=10 tid=0x024e7c00 nid=0xcf0 waiting on condition JNI global references: 563
Ларсен показал, как использовать информацию о потоках, предоставленную jcmd, для устранения тупика.
Ларсен показал получение гистограммы классов из запущенного процесса JVM с помощью jcmd. Это делается с помощью команды jcmd <pid> GC.class_histogram. Далее показано очень маленькое подмножество его вывода (pid этого процесса JConsole 4080 на этот раз).
4080: num #instances #bytes class name ---------------------------------------------- 1: 1730 3022728 [I 2: 5579 638168 3: 5579 447072 4: 645 340288 5: 4030 337448 [C 6: 645 317472 7: 602 218704 8: 942 167280 [B 9: 826 97720 java.lang.Class 10: 3662 87888 java.lang.String 11: 2486 79552 javax.swing.text.html.parser.ContentModel 12: 3220 77280 java.util.Hashtable$Entry 13: 1180 67168 [S 14: 2503 60072 java.util.HashMap$Entry 15: 181 59368 16: 971 43584 [Ljava.lang.Object; 17: 1053 41160 [[I 18: 206 29040 [Ljava.util.HashMap$Entry; 19: 111 27880 [Ljava.util.Hashtable$Entry; 20: 781 18744 java.util.concurrent.ConcurrentHashMap$HashEntry 21: 1069 17104 java.lang.Integer 22: 213 9816 [Ljava.util.concurrent.ConcurrentHashMap$HashEntry; 23: 202 9696 java.util.HashMap 24: 201 9280 [Ljava.lang.String; 25: 24 8416 [[I
Ларсен также продемонстрировал jstat и несколько его полезных функций. Он продемонстрировал использование jstat -gcnew (поведение нового поколения), jstat -precompilation (статистика метода компиляции) и jstat -options (параметры отображения).
В ходе своей презентации Ларсену нужно было преобразовать десятичное число (pid?) В его шестнадцатеричное представление для сравнения его с выводом другого инструмента. Он использовал удобную команду printf «% x \ n» <pid>, чтобы получить шестнадцатеричное представление pid.
Ларсен продемонстрировал использование VisualVM для сравнения двух дампов кучи и просмотра дампов кучи . Он также продемонстрировал VisualVM Profiler .
Ларсен перешел от ранее описанных инструментов, предназначенных для запуска JVM, к инструментам, которые можно использовать для анализа основных файлов JVM. Он вернулся в jstack для анализа содержимого файла ядра.
Ларсен говорил об удаленном доступе к информации JVM через JMX и такие инструменты, как jconsole и jvisualvm. Он продемонстрировал, что jcmd также может быть использован для запуска JMX: ManagementServer.start «с набором параметров». Ларсен считает, что VisualVM и JConsole будут использовать ManagementServer.start, а не Attach API, если они будут реализованы сегодня.
jstat также может подключаться к демону удаленно с помощью jstatd . Нет шифрования или аутентификации с помощью jstatd.
jps и jcmd находят то, что работает в системе, используя «известный файл для каждой JVM»: / hsperfdata_ <пользователь> / <pod> Этот файл создается при запуске JVM и удаляется при завершении работы JVM. Неиспользуемые предыдущие файлы удаляются при запуске, поэтому jps и jcmd, как и сами Java-программы, очистят эти старые.
Attach API «позволяет посылать„команды“для executionin в JVM» , но работает только на локальном компьютере , так и для текущего / того же пользователя. Это то, что используют jcmd и jstack. Затем Ларсен объяснил различные механизмы использования Attach API для Linux / BSD / Solaris (использует создание временных файлов) и Windows (использует внедрение кода). Я использовал Attach API в своем посте Groovy, JMX и Attach API .
Диагностические команды — это «вспомогательные процедуры внутри JVM», которые производят «вывод текста». Они могут быть выполнены с помощью утилиты jcmd (и вскоре через JMX). У каждого из них есть средство с самоописанием: jcmd PerfCounter.print для просмотра необработанного содержимого.
Ларсен показал информативную таблицу, в которой сравниваются подходы «взаимодействия с JVM»: attach , jvmstat , JMX , jstatd и Serviceability Agent (SA). SA «должен использоваться как последнее средство (» обычно для JVM, которая зависла «) и использует» отладчик для чтения информации «.
Ларсен перешел к разговору о будущих инструментах. Он начал эту часть презентации с освещения Java Flight Recorder . Java Flight Recorder — это «встроенный в JVM профилировщик и трассировщик» с «низкими издержками» и «всегда включен». Другие инструменты включают в себя Java Mission Control («графический инструмент, предоставляющий очень подробные сведения о мониторинге во время выполнения»), дополнительные диагностические команды для jcmd («в конечном итоге заменяющих jstack, jmap, jinfo» по разным причинам ), JMX 2.0 («что-то, что мы подбираем» снова; это было начато очень давно «), улучшено ведение журнала для JVM (Предложение по расширению JVM [JEP] 158 ),и протокол обнаружения Java (предвидение предстоящего JEP для этого).
Был задан один вопрос: можно ли увидеть MBeans в VisualVM, как это можно сделать в JConsole. Как я уже писал , есть плагин VisualVM для этого.
Хотя я чувствовал себя несколько комфортно с инструментами командной строки Oracle HotSpot JDK, я был незнаком с jcmd и ценил освещение Ларсена этого. Я узнал и кое-что еще по пути. Моя единственная жалоба заключается в том, что презентация Ларсена (особенно демонстрация) была настолько быстрой и содержательной, что мне хотелось бы увидеть ее снова.
Связанная (но более старая) презентация с некоторым содержимым доступна по адресу http://www.oracle.com/javaone/lad-en/session-presentations/corejava/22260-enok-1439100.pdf.