※当サイトの記事には、広告・プロモーションが含まれます。

JavaでFlight Recorderが良さ気らしいが、jmapでheap dump fileを取得しjhatで解析してみる

www.itmedia.co.jp

www.itmedia.co.jp

www.publickey1.jp

⇧ Dockerの機能が向上するのは良いことかもしれんけど、

www.publickey1.jp

⇧ 他との足並みと言うか、連携は気になりますな...

クラウドベンダーのKubernetesのマネージドサービスでも、同様の対応になっているんかな?

学習コストが嵩むことになるんか...

Javaアプリケーションのメモリーの状態を把握するツールはどんなものがあるのか

Oracleの公式のドキュメントによりますと、

docs.oracle.com

Java言語コード内のリークの診断は難しい可能性があります。通常、アプリケーションの非常に詳しい知識が必要です。さらに、プロセスが反復し、冗長なことがよくあります。この項では、Java言語コード内のメモリー・リークを診断するために使用できるツールについて説明します。

注:
この項で説明したツールに加えて、数多くのサードパーティ・メモリー・デバッガ・ツールが利用可能です。メモリーデバッグ機能を備えた商用ツールの例として、Eclipse Memory Analyzer Tool (MAT)とYourKit (www.yourkit.com)の2つがあります。他にも数多くありますが、推奨される特定の製品はありません。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/memleaks004.html

⇧ という説明があって、

 Java言語コードのリークの診断に使用される2つのユーティリティを次に示します。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/memleaks004.html

  1. NetBeansプロファイラ: NetBeansプロファイラは、メモリー・リークの場所をすばやく見つけることができます。商用のメモリー・リーク・デバッグ・ツールは、大規模なアプリケーション内のリークを特定するのに長い時間がかかる場合があります。しかし、NetBeans Profilerは、このようなオブジェクトが一般的に示すメモリーの割り当てと再利用のパターンを使用します。このプロセスには、メモリー再利用の欠落も含まれます。このプロファイラは、これらのオブジェクトがどこで割り当てられたかをチェックできます(リークの根本原因を特定するには、通常はこれで十分です)。

    詳細は、「NetBeansプロファイラ」を参照してください。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/memleaks004.html

  1. jhatユーティリティ: jhatユーティリティは、予期しないオブジェクトの保持(またはメモリー・リーク)をデバッグする場合に役立ちます。オブジェクト・ダンプを参照し、ヒープ内の到達可能なすべてのオブジェクトを表示し、どの参照がオブジェクトを保持しているかを理解するための方法を提供します。

    jhatを使用するには、実行中のアプリケーションのヒープ・ダンプを1つ以上取得する必要があり、ダンプはバイナリ形式である必要があります。作成されたダンプ・ファイルは、jhatへの入力として使用できます。「jhatユーティリティ」を参照してください。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/memleaks004.html

⇧ 2つのツールを紹介している。

ちなみに、公式ではないと思うのですが、

github.com

⇧「HeapStats」というツールが公開されていて、「Flight Recorder」が利用できない環境では有力な選択肢となっていたようです。

ここ暫くは、更新も止まってしまっているようですが...

Flight Recorderとは?

公式のドキュメントによりますと、

docs.oracle.com

注意: 次の機能のうちいくつかを本番で使用するには商用ライセンスが必要です。商用の機能とそれを有効化する方法の詳細は、Oracle Java SE Advanced製品とOracle Java SE Suite製品を参照してください。

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/enhancements-8.html

  • javaツール:
    • JDK 8u40より前では、Java Flight Recorder (JFR)を使用して特定のJavaアプリケーションの動作記録を作成する場合、アプリケーションの起動時に、-XX:+UnlockCommercialFeatures-XX:+FlightRecorderの両方を指定する必要があります。JDK 8u40では、コマンド行でこれらのオプションを指定する必要はありません。JFRには、アプリケーションの実行時に、商用機能のロックを解除して、JFRを有効化するための、様々な方法が用意されています。

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/enhancements-8.html

⇧「Java Flight Recorder(JFR)」が紹介されているのだけど、有償(商用ライセンス)であるらしいですと。

だったのだけど、Wikipediaの情報によると、

JDK Flight Recorder is an event recorder built into the OpenJDK Java virtual machine. It can be thought of as the software equivalent of a Data Flight Recorder (Black Box) in a commercial aircraft. It captures information about the JVM itself, and the application running in the JVM. There is a wide variety of data captured, for example method profiling, allocation profiling and garbage collection related events. The JDK Flight Recorder was designed to minimize the Observer Effect in the profiled system, and is meant to be always on in production systems. The technology was open sourced in 2018.

Analysis and visualization of flight recordings are normally done using JDK Mission Control.

https://en.wikipedia.org/wiki/JDK_Flight_Recorder

History

JDK Flight Recorder started out as JRockit Flight Recorder, and was originally used as a means to collect data to be used to improve the JVM itself. After Oracle acquired Sun Microsystems, JRockit Flight Recorder was rebranded Java Flight Recorder.

 In 2018 Java Flight Recorder was open sourced and released as part of OpenJDK 11. When open sourced it was rebranded JDK Flight Recorder, due to Java trademark issues.

https://en.wikipedia.org/wiki/JDK_Flight_Recorder

⇧ という歴史的な推移があり、

JRockit Flight Recorder(JFR)

Java Flight Recorder(JFR)

JDK Flight Recorder(JFR) ←これがOSSOpen Source Software)、無償利用できる。

って流れらしく、

Open JDK 11から、OSSOpen Source Software)として利用できるようになったのは「JDK Flight Recorder(JFR)」ということらしい、たぶん。

「Flight Recorder」の詳細なんかは、

openjdk.org

⇧ 上記のページに載ってる感じかな。

そして、

www.infoq.com

⇧「Open JDK 8」でも利用できるようにしてくれてるらしい、ありがたや~。

Oracleの発信してるブログの情報によると、

Arimura: JFRは、2018年にオープンソース・ソフトウェアとしてリリースされました。この点についてはいかがでしょうか。

https://blogs.oracle.com/oracle4engineer/post/java-jdk-flight-recorder-jfr-ja

Grönlund: JFRは長年にわたって商用製品でした。そのため、JFRを使って、本番環境で動作しているプログラムのトラブルシューティングを行うには、ライセンスが必要でした。ただし、開発者がコードを理解して改善するという開発目的であれば、このテクノロジーはもともと無償で利用できました。

https://blogs.oracle.com/oracle4engineer/post/java-jdk-flight-recorder-jfr-ja

2018年に、オラクルはJFRテクノロジーとそれに関連するツール・スイートであるJDK Mission Control(JMC)をOpenJDKコミュニティに寄贈しました。これにより、今は本番環境でも無償で利用できるようになっています。

https://blogs.oracle.com/oracle4engineer/post/java-jdk-flight-recorder-jfr-ja

⇧ とあり、ここで言っているJFRがお互いに噛み合って無さそうなんだが、本番環境で無償で利用できるって言ってるのは「JDK Flight Recorder(JFR)」の話なんではないかと。

JFRは長年にわたって商用製品でした。』って部分は「Java Flight Recorder(JFR)」のことを言ってるんだと思うから、話がごっちゃになってそうなんよね...

Oracleの公式のドキュメントでも、

docs.oracle.com

Note:

Java Flight Recorder requires a commercial license for use in production. To learn more about commercial features and how to enable them please visit http://www.oracle.com/technetwork/java/javaseproducts/.

https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/run.htm#JFRUH164

⇧ と書いてあるので、「Java Flight Recorder(JFR)」の方は、引き続き有償ってことなんでしょうね...

何と言うか、Javaの有償・無償の線引きが相変わらず分かり辛いな...

JavaでFlight Recorderが良さ気らしいが、jmapでheap dump fileを取得しjhatで解析してみる

公式のドキュメントによりますと、

docs.oracle.com

プロセス、コア・ファイル、またはリモート・デバッグ・サーバーの、共用オブジェクト・メモリー・マップまたはヒープ・メモリーの詳細を出力します。このコマンドは試験的なものであり、サポート対象外になっています。

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/jmap.html

オプション

-dump:[live,] format=b, file=filename

Javaヒープをhprofバイナリ形式でfilenameにダンプします。liveサブオプションはオプションですが、これを指定すれば、ヒープ内のアクティブなオブジェクトのみがダンプされます。ヒープ・ダンプを参照するには、jhat(1)コマンドを使用して、生成されたファイルを読み取ります。

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/jmap.html

⇧とあり、オプションで「heap dump file(拡張子が「.hprof」というファイル)」が生成でき、生成されたファイルは「jhat」コマンドで解析するらしい。

「jhat」コマンドの説明を見ると、

docs.oracle.com

Javaヒープを解析します。このコマンドは試験的なものであり、サポート対象外になっています。

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/jmap.html

⇧ とのこと。

どちらも、試験的な機能らしいのと、

qiita.com

⇧ 上記サイト様によりますと、諸々の悪影響があるので、「jmap」や「hprof」は本番利用を控えた方が良い模様...

上記の内容を踏まえた上で、では、jmapとjhatを使ってみますが、

ts0818.hatenablog.com

⇧ 上記の記事の時に試したシェルスクリプトに追加していきます。

■/home/ts0818/work/test_jvm_option/execute_jstat.sh

#!/bin/bash

## ■■■■■■■■■■■■■■■
## ■ 変数定義
## ■■■■■■■■■■■■■■■
jstat_TestJvmOption_tsv="jstat.tsv"
#jvm_start_option_Xmx1024m="-Xmx1024m"
jvm_start_option_Xmx2048m="-Xmx2048m"
jvm_start_option_PrintFlagsFinal="-XX:+PrintFlagsFinal"
interval_millsecond=100
limit_display_result_row_count=20000
datetime_format="%Y/%m/%d %H:%M:%S.%3N"
jvm_java_version="-version"
result_log="result.log"

## ■■■■■■■■■■■■■■■
## ■ ファイル初期化
## ■■■■■■■■■■■■■■■
if [ -e ${jstat_TestJvmOption_tsv} ]; then
  rm ${jstat_TestJvmOption_tsv} && touch ${jstat_TestJvmOption_tsv}
else
  touch ${jstat_TestJvmOption_tsv}
fi

## ■■■■■■■■■■■■■■■
## ■ Java実行
## ■■■■■■■■■■■■■■■
# JVMのバージョンを確認
#execute_java_version=$(java ${jvm_java_version})
java ${jvm_java_version} 2>> ${result_log} &&

# JVM起動時の設定を確認(Xmxの設定のみ表示)
jvm_setting_output=$(java ${jvm_start_option_Xmx2048m} ${jvm_start_option_PrintFlagsFinal} 2>&1 | grep -Ei "MaxHeapSize")
echo "${jvm_setting_output}" >> ${result_log} &&

# TestJvmOptionクラスの実行
java ${jvm_start_option_Xmx2048m} TestJvmOption $1 >> ${result_log} & 
#jps -lm | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gcutil {} ${interval_millsecond} ${limit_display_result_row_count} | awk -F, '{print strftime(`${datetime_format}`,systime()) $1}' >> ${jstat_TestJvmOption_tsv}
#jps -lm | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gc {} ${interval_millsecond} ${limit_display_result_row_count} | awk -F, '{print strftime("%(${datetime_format})",systime()) $1}' >> ${jstat_TestJvmOption_tsv}
#jps -lm | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gc {} ${interval_millsecond} ${limit_display_result_row_count} | awk -v datetime_format="${datetime_format}" -F, '{print strftime(datetime_format, systime()) $1}' >> ${jstat_TestJvmOption_tsv}

# heap dump fileを取得する
jps | grep TestJvmOption | awk '{print $1}' | xargs -I{} jmap -dump:format=b,file=./TestJvmOption_heapdump.hprof {} &

# jstatコマンドの出力をファイルに追記(ヘッダー行を含む)
#jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') ${interval_millsecond} ${limit_display_result_row_count} |
jps | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gc {} ${interval_millsecond} ${limit_display_result_row_count} | 
awk -v datetime_format="${datetime_format}" 'BEGIN { OFS="\t" } {
  split($1, arr, " ");
  ms = sprintf("%.3f", arr[1] / 1000); # ミリ秒を秒に変換し、小数点以下3桁までの文字列にフォーマット
  cmd = "date +\"" datetime_format "\""; # 日時フォーマットを含むdateコマンドを生成
  cmd | getline formatted_date; # dateコマンドを実行してフォーマットされた日時を取得
  close(cmd);

  # 各列をタブ区切りで結合
  output = formatted_date "." ms;
  for (i = 1; i <= NF; i++) {
    output = output "\t" $i;
  }
  print output;
}' >> ${jstat_TestJvmOption_tsv}

#java ${jvm_start_option_PrintFlagsFinal} ${jvm_start_option_Xmx} TestJvmOption $1 >> result.log &
#while jps | grep -q TestJvmOption; 
#do 
#  jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 >> jstat.log; 
#done

#until jps | grep TestJvmOption
#  do 
#    #echo "start jstat TestJvmOption"
#    jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 >> ${jstat_TestJvmOption_tsv}
#  done

#until jps | grep -q TestJvmOption
#  do 
#    jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 | awk -F, '{print strftime("%Y/%m/%d %H:%M:%S",systime()) $1}' >> ${jstat_TestJvmOption_tsv}
#  done

#until jps | grep -q TestJvmOption
#  do
#    jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 | perl -wn -e '$d = `date "+%Y/%m/%d %H:%M:%S"`; chomp $d; print "$d | $_"' >> ${jstat_TestJvmOption_tsv}
#  done


#jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 | awk -F, '{print strftime("%Y/%m/%d %H:%M:%S",systime()) $1}';

そしたらば、「WSL 2(Windows Subsystem for Linux 2)」のUbuntuを起動しておき、SSH接続で環境にログイン。

⇧「heap dump file(拡張子が「.hprof」)」が生成されてます。

「WSL 2(Windows Subsystem for Linux 2)」のUbuntuのOpen JDKにjhatが同梱されていなかったので、ホスト側(Windows 10 Home)に「heap dump file(拡張子が「.hprof」)」をダウンロードして、ホスト側のJDKに同梱されてたjhatで実行。

ブラウザで「http://localhost:7000」にアクセス。

⇧「Show heap histogram」をクリックすると、どのJavaオブジェクトがヒープメモリの使用量が多いのかザックリと把握できる模様。

とは言え、上記の内容だとほとんど何も分からんような気がするので、やはり、「JDK Flight Recorder(JRF)」を使っていった方が良いんかね...

次回は、「JDK Flight Recorder(JFR)」の使い方を調べていきますかね。

毎度モヤモヤ感が半端ない...

今回はこのへんで。