#chiroito ’s blog

Java を中心とした趣味の技術について

JJUG CCC 2020 Fall で「パフォーマンスのトラブルシュート入門」という内容で喋ってきました

2020年11月7日(土)に開催された JJUG CCC 2020 Fall にて「パフォーマンスのトラブルシュート入門」というタイトルで話をしてきました。

動画はこちらです。


CCC 2020 Fall A02-パフォーマンスのトラブルシュート入門

スライドはこちらになります。

speakerdeck.com

Twitterの履歴はこちらです。#jjug_ccc_a というハッシュタグの 11:00~12:00 の間が私のセッション分になります。

twitter.com

Twitter などでいただいた質問は以下になります。当日回答した質問もありますが、質問部分は公開されないと思うのでこちらに記載しておきます。類似の質問はこちらでまとめさせていただきました。

JFR Stream で、jfrダンプがしなくてもモニタリングできるようになったらいいですね。でもEventの始まりと終わりで異常処理考慮しなくちゃいけないの大変ではありませんか?

JFR Event Stream では、記録の終了後にダンプされた JFR ファイルを読み込む方法に加え、稼働中の記録を内部的に作成しているチャンクファイルから読み込む方法があります。後者の方法を使うことで JFR ファイルをダンプしないでもモニタリングできるようになっています。このチャンクファイルはデフォルトで 1 秒ごとに記録を書込んでいます。そのため JFR Event Stream で読み込む際にはイベントが記録されてから最大で 1 秒遅れるためリアルタイムには処理されないため注意してください。 イベントの書込み処理の実装では、例外処理も気にする必要があります。イベントを書込む処理は基本的には try - catch -finally で囲まれていることが多いので、その場合は finally できちんと書込むようにして下さい。参考までに私が開発している Jfr4Jdbc では JDBC のライブラリ群が投げる SQLException に加えて、非チェック例外である RuntimeException も気にするように実装しています。

JFRが使えるのはJDLK11からですか?

OpenJDK で JDK Flight Recorder が使えるのは OpenJDK 11 からです。その後、OpenJDK 8 update 262 にバックポートされました。前身である Oracle JDK の Java Flight Recorder は HotSpot では Java 7 update 40 から使用できます。Oracle JDK の JRockit の場合はそれ以前のバージョンでも使用できます。

クラウドのサーバーレス環境でもフライトレコーダーって使えるんですか?
コンテナ系のサービスだと、どう監視するのがいいんだろ……?

Java の標準機能のためどの様な環境でも使用できます。サーバレスやコンテナだから使えないという機能ではありません。ただし、ダンプファイルをどこかの永続化ストレージに格納する必要があるので、サーバレスのプロセスが停止する際にオブジェクトストレージなどへアップロードする処理が必要です。

Kubernates や OpenShift などのコンテナオーケストレーションツールを使用する場合は Red Hat から JFR のオペレータが出ているためこちらをご利用下さい。コンテナオーケストレーションツールを使用していない場合は、永続ストレージに JFR や収集した情報を記録する様にして下さい。

この辺の内容のまとまった情報のあるおすすめの書籍などあれば

概要や使い方を紹介した資料はいくつかありますが、JFR の全体や内部構造などのまとまった資料はありません。様々なイベントで Oracle 社のエンジニアが発表している資料が良いと思います。これらの動画は Youtube などで公開されています。

JFR の利用が原因でパフォーマンスが劣化するといったことは考えられますでしょうか?
フライトレコーダーって本番で動きっぱなしにしてもリソース的に大丈夫なものなんでしょうか?(環境次第な気もしますが)
パフォーマンス計測をし続けることにどの程度負荷かかるんだろな

JFR のイベントを書込む処理を記述していても、JFR が動いていなければパフォーマンスは劣化しません。これはイベントの書込み処理などイベントに関わるコードが JIT コンパイラによって取り除かれるように実装されているためです。 JFR を動かす場合には default と profile という標準設定群があります。一般的な使用方法では default で良く、さらに詳細に分析したい場合には profile を使用します。default では一般的な使用方法では 1~2% の負荷になります。この負荷はイベントを書込む頻度などにもよります。profile ではもう少し大きな負荷になるため、本番環境での使用は良く確認してから試しましょう。

JFRは常に有効にしておくものでしょうか?それとも何か発生したら調査のために有効にするものでしょうか?

JFR は常に有効にしておくことをオススメします。JFR は内部的に循環バッファを使用しています。そのため、新しいイベントが書込まれると過去の分はどんどん削除されていきます。そのため、問題が起きたときにダンプすれば過去に遡ってバッファに残っている分の情報が入手できます。また、正常時のダンプも忘れないように取得しておきましょう。

FR カスタム Event を備えたフレームワークはどんなものがあるのでしょうか

Oracle 社の Oracle WebLogic Server (WLS) と私が開発している Jfr4Jdbc があります。WLS では Java EE に関するイベントやそれに付随するシステムのイベントを書込んでくれます。これは、特別な処理は不要で WAR ファイルや EAR ファイルをデプロイするだけで各種イベントが記録されます。 また、Jfr4Jdbc は JDBC に関するイベントを書込みます。これらのイベントにはコネクションの接続や切断、クエリの実行、コミット、ロールバックといった JDBC の API を呼び出すだけで記録されるようになります。また、コネクションプールの使用数や割り当て待ち数といった情報も記録されます。これらの情報を取得するために必要な事はデータソースをラップするか、JDBC の 接続 URL に jfr: を追加するだけです。

JFRで記録できるイベントの一覧はどこかで確認できるんでしょうか?

JFR で記録できるイベントは日々追加されています。一覧が記録されている情報はありません。一覧を確認するには、JFR を遊行した Java プロセスを起動し、JDK Mission Control で接続して記録を開始し始めるときに一覧を出せます。また、一度 JFR ファイルをダンプし、その情報に含まれる[環境]-[記録]から確認できます。

JFRが使えないレガシー環境ではどれがおすすめですか?

今回の講演でも紹介しましたが、JFR が使えないレガシー環境では、OS、JVM、アプリケーションできちんとログやメトリクス、リソースといった情報を取るようにしなければなりません。そのためには JVM に標準で含まれているログ機能や JMX/MBean といった物を有効活用するようにします。また、アプリケーションサーバ(フレームワーク含む)がきちんと情報を得られる物を選択しましょう。

OS情報の計測によく使うツールやノウハウがありましたら教えてください。

Linux の場合は iostat/mpstat/sar など OS が使用するリソースの情報を収集系のツールがあります。これらのツールを適切に使うことがオススメです。CPU等の情報は、まとめて取るのではなく論理CPUやディスクなどを個別に収集するようにオプションを設定しましょう。時刻情報が付かないツールも多いので、収集時は時刻情報も含まれるよう工夫すると良いです。 以下がサンプルです(最近動かしてないので動くかは不明ですが・・・)

RESULT_CASE=sample
DATE_FORMAT="%Y %m %d %H %M %S"
INTERVAL_SEC=10
TIMES=1080
mpstat -P ALL ${INTERVAL_SEC} ${TIMES} | awk -v df="${DATE_FORMAT}" '(NR==3 || (NR>3 && /^[0-9].+[0-9]$/)){print strftime(df), $0}' >> ${RESULT_CASE}_mpstat_`date +\%Y\%m\%d`.log &
vmstat -n -S m ${INTERVAL_SEC} ${TIMES} | awk -v df="${DATE_FORMAT}" '(NR>=2){print strftime(df), $0}' >> ${RESULT_CASE}_vmstat_`date +\%Y\%m\%d`.log &
iostat -mx -d -p ALL ${INTERVAL_SEC} ${TIMES} | awk -v df="${DATE_FORMAT}" '(NR==3 || (NR>3 && /.+[0-9]$/)){print strftime(df), $0}' >> ${RESULT_CASE}_iostat_`date +\%Y\%m\%d`.log &