JMX で Java Flight Recorder (JFR) を実行する
Java Flight Recorder (JFR) は Java Mission Control (jmc) や jcmd コマンドから実行できますが、今回は以下の MBean を使って JMX から実行してみます。
- com.sun.management:type=DiagnosticCommand
この MBean は以下のような操作を備えており(戻り値は全て String)、jcmd コマンドと同じ事ができるようです。
- jfrCheck
- jfrDump
- jfrStop
- jfrStart
- vmCheckCommercialFeatures
- vmCommandLine
- vmFlags
- vmSystemProperties
- vmUnlockCommercialFeatures
- vmUptime
- vmVersion
- vmNativeMemory
- gcRotateLog
- gcRun
- gcRunFinalization
- gcClassHistogram
- gcClassStats
- threadPrint
(a) JFR の実行
JMX を使う方法はいくつかありますが、今回は Attach API でローカルの VM へアタッチし、startLocalManagementAgent
メソッドで JMX エージェントを適用する方法を用いました。
DiagnosticCommand には java.lang.management.ThreadMXBean のようなラッパーが用意されていないようなので GroovyMBean
を使う事にします。
jfrStart
の引数は jcmd コマンドと同じものを String 配列にして渡すだけのようです。(jfrStart 以外も基本的に同じ)
また、JFR の実行には Commercial Features のアンロックが必要です。
jfr_run.groovy
import com.sun.tools.attach.VirtualMachine import javax.management.remote.JMXConnectorFactory import javax.management.remote.JMXServiceURL def pid = args[0] def duration = args[1] def fileName = args[2] // 指定の JVM プロセスへアタッチ def vm = VirtualMachine.attach(pid) try { // JMX エージェントを適用 def jmxuri = vm.startLocalManagementAgent() JMXConnectorFactory.connect(new JMXServiceURL(jmxuri)).withCloseable { def server = it.getMBeanServerConnection() // MBean の取得 def bean = new GroovyMBean(server, 'com.sun.management:type=DiagnosticCommand') // Commercial Features のアンロック (JFR の実行に必要) println bean.vmUnlockCommercialFeatures() // JFR の開始 println bean.jfrStart([ "duration=${duration}", "filename=${fileName}", 'delay=10s' ] as String[]) } } finally { vm.detach() }
実行例
apache-tomcat-9.0.0.M4 へ適用してみます。
Tomcat 実行
> startup
以下の環境で実行しました。
- Groovy 2.4.6
- Java SE 8u92 64bit版
JFR 実行
> jps 4576 Jps 2924 Bootstrap > groovy jfr_run.groovy 2924 1m sample1.jfr Commercial Features now unlocked. Recording 1 scheduled to start in 10 s. The result will be written to: C:\・・・\apache-tomcat-9.0.0.M4\apache-tomcat-9.0.0.M4\bin\sample1.jfr
jfrStart は JFR の完了を待たずに戻り値を返すため、JFR の実行状況は別途確認する事になります。
出力結果 Recording 1 scheduled
の 1
が recoding の番号で、この番号を使って JFR の状態を確認できます。
ファイル名を相対パスで指定すると対象プロセスのカレントディレクトリへ出力されるようです。 (今回は Tomcat の bin ディレクトリへ出力されました)
(b) JFR の状態確認
JFR の実行状況を確認するには jfrCheck
を使います。
下記では recording の番号を指定し、該当する JFR の実行状況を出力しています。
jfrCheck の引数が null の場合は全ての JFR 実行状態を取得するようです。
jfr_check.groovy
import com.sun.tools.attach.VirtualMachine import javax.management.remote.JMXConnectorFactory import javax.management.remote.JMXServiceURL def pid = args[0] String[] params = (args.length > 1)? ["recording=${args[1]}"]: null def vm = VirtualMachine.attach(pid) try { def jmxuri = vm.startLocalManagementAgent() JMXConnectorFactory.connect(new JMXServiceURL(jmxuri)).withCloseable { def server = it.getMBeanServerConnection() def bean = new GroovyMBean(server, 'com.sun.management:type=DiagnosticCommand') println bean.jfrCheck(params) } } finally { vm.detach() }
実行例
recording 番号(下記では 1
)を指定して実行します。
実行例1 (JFR 実行中)
> groovy jfr_check.groovy 2924 1 Recording: recording=1 name="sample1.jfr" duration=1m filename="sample1.jfr" compress=false (running)
実行例2 (JFR 完了後)
> groovy jfr_check.groovy 2924 1 Recording: recording=1 name="sample1.jfr" duration=1m filename="sample1.jfr" compress=false (stopped)
今回作成したサンプルのソースは http://github.com/fits/try_samples/tree/master/blog/20160519/