JJUG CCC 2021 Spring の「JFR などのツールを用いて FullGC や OOME の原因を特定する流れ」の登壇資料です。
JFR などのツールを用いて FullGC や OOME の原因を特定する流れ# jjug #jjug_ccc髙市 智章 (Tomoaki Takaichi)May, 23, 2021 JJUG CCC 2021 Spring
View Slide
自己紹介@Takaichi00tomoaki.takaichi.5・髙市 智章(タカイチ トモアキ)・Java でのシステム開発・CI / CD・Container / k8s・アジャイル開発実践共著: クリーンなコードへのSonarQube即効活用術www.amazon.co.jp/dp/B086ML43DH
❏ 以前、検証では成功した Java のプログラムが商用で失敗するということを経験した❏ PreparedStatement / ResultSet の close 忘れによりE (OOME) が発生してしまうことが原因だった本日お話すること参考: 【JUGナイトセミナー】検証では成功した Java のパッチが商用でコケた件
❏ 前回事例を発表した LT では、プログラムに問題がありそうな箇所をあらかた特定できていたため、GC Viewer を用いてトライアンドエラーで問題を解決できた❏ しかし Java アプリケーションを運用する上で実際に発生する問題には、原因に見当がつかないものも多いのでは本日お話すること
❏ 今回ではこの問題に対して「もし原因の見当すらついていなかった場合どのように対処するか?」という観点で、JFR (JDKFlight Recorder) などのツールを用いながら問題解決をしていく流れを発表する❏ 以下のような方を対象に、この発表が参考になれば幸い❏ JFR などのツールは聞いたことがあるが使ったことがない、これから使ってみたいので概要や使い方を知りたいという方❏ プログラミング言語としての Java だけではなく、JVM / GC / Heap /Stack / Thread などの技術にも触れてみたいという方本日お話すること
❏ 発表内容には誤りが無いよう、様々な文献などを参考にして最善を尽くしておりますが、まだまだ知識が足りないと感じています❏ もし発表内容に誤りがある / もっと効率的な調査方法やツールがあるといった場合には、その旨をご連絡・ご発信いただけますと幸いです発表内容にあたってhttps://www.oreilly.co.jp/books/images/picture_large978-4-87311-718-8.jpeg
問題が発生した事例の紹介
❏ 今回のサンプルアプリケーションは Github にて公開中❏ Java と Docker の環境があれば再現が可能問題が発生したアプリケーションのサンプルTakaichi00 / jfr-sample: https://github.com/Takaichi00/jfr-sample
❏ 以下のような java のプログラムを作成問題が発生したアプリケーションid name address1 aaa NULL2 bbb NULL... … ...name register_eventaaa 1aaa 2bbb 1.jar① `address` が NULL の一覧取得② `name` に対応する `register_event` を取得 / 判定処理insert.sql④ TABLE_A に `name` に対応する `address` を insertする SQL ファイルを作成TABLE_ATABLE_Bregister_eventaddress1 1232 456... ...TABLE_C③ `register_event` に対応する`address` を取得 / 判定処理
問題の実装コードと実行コマンドTEBLE_A から SELECT したデータを List に格納List の数だけ TEBLE_B に対してSELECT を実行TEBLE_B で取得できた結果の数だけ、再度 TEBLE_C に対してSELECT を実行(※) 今回は再現のため、簡略的にTABLE_C ではなく TABLE_B にSELECT を実行
❏ ローカル PC (メモリ 16GB) から、検証用のデータが入った検証用 DB へ向けてこのパッチを起動したところ、問題なく実行が完了した (データ量は数十件程度)検証の構成では成功検証環境成功メモリ: 16GB
❏ 商用環境では VM 上 (メモリ 1GB) で作成したパッチを実行した。しかし処理が全然完了しなかった❏ 商用のデータ量は検証環境よりは多いが、たかだか1万件未満程度のもの商用環境では失敗商用環境失敗メモリ: 1GB
❏ java -jar コマンド実行時の引数に -Xlog:gc* を追加してGC ログの詳細を表示してみる❏ すると FullGC が多発していることがわかったGC ログを出してみる$ java -Xlog:gc* -jar hoge.jar...[81.318s][info][gc ] GC(29) Pause Full (G1 Evacuation Pause)...[81.318s][info][gc,cpu ] GC(29) User=0.05s Sys=0.00s Real=0.02s※ Java11, GC は G1GC を使用
❏ JVM にはエルゴノミクスというプロセスがあり、マシンのスペックに応じてヒープサイズが自動決定される❏ デフォルトでは以下の設定となっているヒープオプションを追加する初期ヒープ・サイズ 物理メモリーの 1/64 (最大1GB)最大ヒープ・サイズ 物理メモリの 1/4 (最大1GB)❏ 今回 jar を実行した商用環境では、メモリが 1GB となっていたため、最大でも250 MB 程度しかヒープを使うことができない状態❏ 一時対処として jar 実行時に、-Xms512M -Xmx512M のようにヒープサイズを設定することで、時間はかかったが処理を完了させることができた
❏ 後日コードレビューにより、PreparedStatement /ResultSet の close 忘れが発覚❏ 本当にこれらが原因なのか検証するため、まずは GCViewer を用いて簡易的に解析そもそも実装コードが悪い
❏ GC ログから、GC の発生やそれに伴うヒープの推移などが視覚的に表示できるツール❏ もともと tagtraum 社によって開発されていたが、2008年で開発がストップされており、現在は個人の方が開発を続けているツールGC Viewer とはGC Viewer のダウンロード:https://github.com/chewiebug/GCViewer/wiki/Changelog
❏ 似たようなプログラムを実装し、ヒープサイズ 20MB と指定し、GC ログを出力するようにして実行した結果、処理が進むに連れ FullGC が発生して最終的に OutOfMemoryErrorが発生した (データ量約1万件)問題のプログラムを GC Viewer を用いて解析$ java -Xlog:gc:./gc.log \-Xlog:gc* \-Xms20M -Xmx20M -jar hoge.jarGC ログをファイルに出力して実行し、GCViewer からログファイルを読み込む
❏ PreparedStatement / ResultSet を close するよう処理を修正したところ FullGC は発生せず適切にメモリが開放されて処理が継続できた (データ量約1万件)解決方法
問題が発生した事例のまとめ❏ ヒープサイズは指定して実行しないと、マシンによって実行結果に差異が出る可能性 (エルゴノミクス)❏ DB 接続処理などで実施すべき close 処理は怠らない❏ できるなら商用環境のデータ量でテストを実施する❏ GC ログ, GC Viewer を用いてアプリケーションの内部状態を手軽に確認できる
もし問題点がわからなかったらどのように対処するか?
❏ プログラムに問題がありそうな箇所をあらかた特定できていたため、GC Viewer を用いてトライアンドエラーで問題を解決できた❏ しかし実際に発生する問題には、原因に見当がつかないものも多い❏ GC log, GC Viewer だけでは原因追求は難しい❏ そこで「もし原因の見当すらついていなかった場合どのように対処するか?」という観点でこのプログラムを解析していく問題の特定をどのようにするか
OutOfMemoryError のメッセージを確認する
❏ アプリケーションの実行結果では「Exception in thread"main" java.lang.OutOfMemoryError: Java heap space」というエラーメッセージが表示された❏ よって今回では Heap Memory がいっぱいになったということがわかるOOME が発生した実行結果のログを確認するアプリケーションの実行結果:
❏ しかしこの情報だけでは、単に使用中のオブジェクトが多すぎてヒープに収まらなくなったのか、不使用になったオブジェクトを開放せず、新しいオブジェクトの割当を続けていることが原因なのかがわからない❏ 単にヒープサイズを拡大したら問題なく動いた、というのは本質的な解決策ではないOOME が発生した実行結果のログを確認する
補足: OOME の種類Exception in thread "main" java.lang.OutOfMemoryError: # Heap Memory がいっぱいの場合“Java heap space”# Native Memory がいっぱいの場合“unable to create new native thread”# Metaspace がいっぱいの場合“Metaspace”# GC に時間がかかりすぎている場合“GC Overhead limit exceeded”… などなどJava Platform, Standard Editionトラブルシューティング・ガイド, 3.2 OutOfMemoryError例外の理解:https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/memleaks002.html
JFR (JDK Flight Recorder)での解析
❏ JFR (JDK Flight Recorder) とは、OpenJDK 11 から無償利用できるようになった、Java アプリケーションの障害分析ツール❏ 現在では OpenJDK 8 update 262 にバックポートされている❏ CPU / ヒープ / GC / Thread … など、Java アプリケーションに関する様々なメトリクスを確認することができるJFR (JDK Flight Recorder) とは
❏ 実行時に “-XX:StartFlightRecording=parameter=value” オプションを指定することで JFR を有効にすることが可能❏ 以下のコマンドの例では、アプリケーション終了後に .jfr をダンプルすることができるJFR を有効にしてアプリケーションを起動するオプションの詳細 (日本語):https://koduki.github.io/docs/book-introduction-of-jfr/site/03/01-recording-jfr.html$ java \-XX:StartFlightRecording=\dumponexit=true,\ # JVM プロセスがシャットダウンした時にファイルにダンプするfilename=./output/jdbc-bad-sample-FULLGC.jfr,\-Xms20M -Xmx20M -jar ./target/jdbc-bad-sample-FULLGC.jfr
❏ .jfr ファイルを取得するには、Java の実行オプションで指定する以外にも、 jcmd コマンドを利用した以下のような方法がある補足: JFR の取得方法$ java -XX:StartFlightRecording -Xms20M -Xmx20M -jar ./target/jdbc-bad-sample-FULLGC.jar# 1分間の間 JFR の記録を実行し、ファイルに出力する$ jcmd JFR.start duration=1m filename=recording.jfr# JFR の記録の実行中にデータをファイルに書き込み、記録の実行を継続する$ jcmd JFR.dump filename=recording.jfrJava Platform, Standard Edition Java Flight Recorderコマンド・リファレンス, 2 診断コマンド・リファレンス:https://docs.oracle.com/javacomponents/jp/jmc-5-5/jfr-command-reference/diagnostic-command-reference.htm
❏ 作成された .jfr ファイルは JMC (JDK Mission Control)を使って視覚的に確認することができる❏ JMC のダウンロードは Oracle の公式サイト や 各 JDKのディストリビューションから可能❏ Oracle:(https://www.oracle.com/java/technologies/javase/products-jmc8-downloads.html)❏ AdoptOpenJDK: (https://adoptopenjdk.net/jmc.html)❏ Zulu: (https://jp.azul.com/products/zulu-mission-control/)JMC (JDK Mission Control) で結果を解析するhttps://avatars.githubusercontent.com/u/34046054?s=200&v=4
❏ JMC を起動し、「ファイル(F)」→「ファイルを開く」から、生成した jdbc-bad-sample-FULLGC.jfr を選択すると、分析結果が表示されていることを確認できるFullGC が発生するアプリケーションを JFR / JMC で で解析
JMC で Java アプリケーションを解析する❏ 問題なありそうな箇所は、赤い「!」アイコン で表示されている❏ まずは左メニューから「アウトライン」→「Java アプリケーション」を選択し、アプリケーションの解析結果を確認する
JMC で Java アプリケーションを解析する❏ 「Java アプリケーション」ではアプリケーションの全体概要を表示することが可能❏ スレッド / CPU 使用率/ ヒープ使用率 / GC 起因等によるユーザーコードの停止時間 などを確認することができる
JMC で Java アプリケーションを解析するスレッド情報: main スレッドでプロファイリングが実施されている。対象のスレッドはクリックで選択可能ヒープ使用量: 徐々にヒープが上昇し続けている停止: 実行時間と共に、GC 起因等によるユーザーコードの停止時間が長くなってきている
JMC で Java アプリケーションを解析するヒープ: 時間とともに上昇を続けており、GC が発生しても適切にメモリが開放されていない❏ 「メモリー」では、マシン全体のメモリサイズ、使用されているヒープ、GC の発生頻度と時間などが確認できる❏ ヒープ統計を有効にしていないので、ヒープに関する詳細な統計情報は表示されていないガベージ・コレクション (GC): 時間とともにに GC にかかる時間が増加している
JMC で Java アプリケーションを解析する❏ 「メモリー」→ 「ライブ・オブジェクト数」から、GC の対象外となるオブジェクト(OOME の原因となりそうなオブジェクト) を確認することができる❏ しかしヒープ統計を有効にしていないので、詳細な統計情報は表示されていないライブオブジェクトとは... ガベージコレクタの仕組みを理解する:https://www.atmarkit.co.jp/ait/articles/0404/02/news079.html
JMC で Java アプリケーションを解析する❏ 「JVM 内部」→ 「ガベージコレクション」から、GC の統計情報を取得することができるGC ID / コレクタ名: “G1Full” が表示されており、Full GC が頻発している最長の一時休止 / 一時停止合計: 実行時間の経過に伴い、Full GC が頻発しているためアプリケーションの休止時間も増加傾向にある
ここまでのまとめ❏ アプリケーションの実行時間が進むにつれ...❏ FullGC が徐々に発生し始め、頻度が高くなる❏ ヒープ使用率が徐々に増加している❏ アプリケーションの休止時間が多くなる❏ しかし原因の特定までは至っていない
❏ JFR / JMC を用いた解析で、徐々にヒープ使用率が上昇していることがわかる❏ 単にヒープサイズが足りないのが原因ではなさそう。ヒープを増やしても問題の本質的な解決になならない❏ 使用されているヒープのうち、何がヒープ上昇の原因となっているかを追求するNext Action
ヒープダンプを取得しMemory Analyzer で解析
❏ ヒープダンプを取得することで、ヒープの内訳を確認する❏ ヒープダンプの取得方法は複数あるが、まずは OOME 発生時にヒープダンプを生成してくれる“-XX:+HeapDumpOnOutOfMemoryError” をつけて実行してみるヒープダンプ を取得する$ java \-XX:+HeapDumpOnOutOfMemoryError \-XX:HeapDumpPath=./output \-Xms20M -Xmx20M -jar ./target/jdbc-bad-sample.jar
❏ 作成された .hprof ファイルは Memory Analyzer で解析することができる❏ Memory Analyzer は Eclipse Foundation のページ(http://www.eclipse.org/mat/) からダウンロードが可能.hprof ファイルを Memory Analyzer で解析するhttp://www.eclipse.org/mat/
❏ 「File」→「Open File」から、先程作成された .hprof ファイルを開くと、以下のように解析された画面が表示される❏ 円グラフの部分をクリックすると、どのオブジェクトがどのくらいの割合を占めているかを確認することができる.hprof ファイルを Memory Analyzer で解析する
❏ このヒープダンプが取られているのは OOME 発生時なので、一番割合を占めているものが怪しい❏ 一番割合が多い部分をクリックすると、com.mysql.cj.jdbc.ConnectionImpl との記載がある。よって mysqlの接続処理に問題がありそうという目星をつけることができる。.hprof ファイルを Memory Analyzer で解析する
❏ 「Actions」→「Histogram」を選択すると、Class の一覧と Object 数、Object 単体のサイズ (ShallowHeap)、Object が持つ他の Object を含めたサイズ(Retained Heap) がリストで表示される.hprof ファイルを Memory Analyzer で解析する
.hprof ファイルを Memory Analyzer で解析する❏ Retained Heap の多い順にリストを並び替えるとjava.lang.Object, CopyOnWriteArrayList の下にcom.mysql.cj.jdbc.~ のクラスが続いている❏ ここから MySQL の Connection 周りで大きく heap を使っていることがわかる
.hprof ファイルを Memory Analyzer で解析する❏ 一番 heap を使っている java.lang.Object の "MergeShortest Path to GC Roots" を開くと、生存している(GC の対象外となった) Object と、その Object が参照している Object 群を階層構造で表示できる
.hprof ファイルを Memory Analyzer で解析する❏ Main Thread を expand していくと、”com.mysql.cj.jdbc.~” のObject が大量に存在していることがわかる❏ ”ConnectionImpl”❏ “ClientPreparedStatement”❏ “ResultSetImpl”❏ よって MySQL DB へ接続する処理で、Connection 処理が適切にclose されていないのでは? という仮説をたてることができる
仮説で浮かび上がった問題を解消し、再度解析を実行する
❏ 問題が発生したアプリケーションのコードのPreparedStatement, ResultSet が利用されている部分を確認すると、確かに close 処理が記述されていない❏ close 処理を実施したうえで、再度解析を実施してみるOOME の原因を解消して再度解析するPreparedStatement とResultSet の close 処理をそれぞれ記載
❏ PreparedStatement と ResultSet の close 処理を行ったアプリケーションを実行すると、問題なく処理が完了した❏ JFR で解析してみると以下のように、適切にメモリが開放されていることがわかるOOME の原因を解消して再度解析するOOME 発生時に表示されていた赤い「!」マークも今回は表示されていない※ 「環境」→「プロセス」に表示されているものはローカル PC の CPU 使用率に起因し、マシンで実行している他のアプリケーションの影響も受けるので今回は対象外と考える
❏ 「ガベージ・コレクション」を確認しても、FullGC は発生しておらず、休止期間も低下していることがわかるOOME の原因を解消して再度解析する
❏ OOME は発生しないので、前回とは別の方法でヒープダンプを取得する❏ 処理の後半で jmap コマンドを実行し、ヒープダンプを取得して Memory Analyzer で解析する# ヒープダンプの生成前に FullGC を実施$ jmap -dump:live,file= # 以下のコマンドでも ヒープダンプを取得可能$ jcmd GC.heap_dump OOME の原因を解消してヒープダンプを取得
❏ 取得できた .hprof ファイルを Memory Analyzer で開いてみると、ヒープ全体のサイズは 6.6MB と 20MB の上限まで余裕があり、円グラフで一番割合が多い部分は java.lang.Thread (main) となっている❏ OOME 発生時に Heap の多くの割合を占めていたcom.mysql.cj.jdbc.ConnectionImpl は見られないOOME の原因を解消してヒープダンプを取得※ 灰色の部分は Remainder という、上位の割合を占めないその他の Heap を合計したもの
❏ 前回同様「Histgram」から一番 heap を使っているjava.lang.Object の "Merge Shortest Path to GC Roots" を開いて見てみても、”com.mysql.cj.jdbc” のオブジェクトは見られない → OOME の問題は解消と判断できるOOME の原因を解消して ヒープダンプ を取得
ここまでのまとめ❏ JFR を有効にし、JMC を用いて解析を行うことでアプリケーションがどのようなヒープ、GC の傾向によってOOME エラーが発生したかを把握❏ OOME 発生時にヒープダンプを取得することで、 OOMEの原因となっているオブジェクトを特定❏ 原因と思われる箇所を修正し、再度同様の解析を実施して問題が解消されていることを確認
JFR をより活用する
JFR でヒープ統計を有効にする
❏ 先程の例では JFR で「メモリー」に関する詳細情報は確認できなかったが、 “path-to-gc-roots=true” オプションをつけて実行することでヒープ統計を有効にすることができる❏ このオプションは記録の終了時にガベージ・コレクション(GC)のルートへのパスを収集するためのフラグJFR でヒープ統計を有効にする$ java \-XX:StartFlightRecording=\dumponexit=true,\filename=./output/jdbc-bad-sample-FULLGC.jfr,\path-to-gc-roots=true \-Xms20M -Xmx20M -jar./target/jdbc-bad-sample-FULLGC.jfr$ jcmd JFR.dump filename=recording.jfrpath-to-gc-roots=true※ -XX:StartFlightRecording オプションを付けて起動
❏ “path-to-gc-roots=true” を有効にすると、実行の開始時と終了時に Old ガベージ・コレクションがトリガーされるため、通常よりもパフォーマンスの悪化が発生する❏ パフォーマンスの遅延を検証する際や、パフォーマンスを悪化させてはいけない環境での実行は控えるJFR でヒープ統計を有効にする際の注意点4 JFRを使用したパフォーマンスの問題のトラブルシューティング:https://docs.oracle.com/javase/jp/9/troubleshoot/troubleshoot-performance-issues-using-jfr.htm
❏ “path-to-gc-roots=true” を有効にすると、「メモリー」から「ヒープのライブ・セット傾向」「ライブ・オブジェクト数」が表示されるJFR でヒープ統計を有効にする
❏ 「ヒープのライブ・セット傾向」では、メモリリークが発生するオブジェクトの候補が表示されるJFR でヒープ統計を有効にする1つめのリーク候補:“AbandonedConnectionCleanupThread$ConnectionFinalizerPhantomReference.networkResources”が表示されている2つめのリーク候補: ”JVMCI” から参照されている HashMap オブジェクトが対象になっている。JVMCI は Java で JIT を作るためのインターフェースなので、今回は関係なさそう(https://www.sakatakoichi.com/entry/2017/11/27/193000)
❏ 「ライブ・オブジェクト数」では、GC の対象外となるオブジェクト (OOME の原因となりそうなオブジェクト) が表示されるJFR でヒープ統計を有効にするライブ・オブジェクトのサンプル:AbandonedConnectionCleanupThread というクラスに関するオブジェクトが赤文字で表示されており、OOME の原因と考えられる
❏ ヒープ統計の情報から、 AbandonedConnectionCleanupThread のオブジェクトが増え続けているため、このオブジェクトが OOME の原因ではないかという仮説も立つ❏ 調べて見ると AbandonedConnectionCleanupThread は、放置された MySQL DB の Connection を close してくれる Thread で、度々メモリリークの原因にもなっているよう❏ 最初に調べたときは、この AbandonedConnectionCleanupThreadのバグを踏んだのかとも考えた補足: AbandonedConnectionCleanupThreadMySQL Connector/J (JDBC ドライバ)の罠まとめ: https://saiya-moebius.hatenablog.com/entry/2014/08/20/2304
❏ しかし JMC の「Java アプリケーション」→「スレッド」を選択し、mysql-cj-abandoned-connection-cleanup のスレッドを確認すると、特にこのスレッドは実行されていないように見える補足: AbandonedConnectionCleanupThread
❏ また、ヒープダンプを見た限りでは MySQL の Connection 周りのオブジェクトが OOME の原因だった❏ よって、AbandonedConnectionCleanupThread が直接の原因ではなく、Close できない Connection が原因で 、AbandonedConnectionCleanupThread のオブジェクトが増加しているためこのような結果になったと考えられる❏ OOME の調査をする際は、JFR だけの情報だけではなく、ヒープダンプなどの情報も合わせて多角的に調査する必要があるのでは補足: AbandonedConnectionCleanupThread※ この件については自分なりに調査しましたが、推測の域を出ない部分があるので詳しい方がいらっしゃいましたらご連絡いただけますと幸いです。AbandonedConnectionCleanupThread の実装:https://github.com/LucasPln/ProjetOOSE/blob/master/lib/mysql-connector-java-5.1.48/src/com/mysql/jdbc/AbandonedConnectionCleanupThread.javaReferenceQueue javadoc: https://docs.oracle.com/javase/jp/8/docs/api/java/lang/ref/ReferenceQueue.html
JFR でより詳細な情報を取得する
❏ JFR を実行する際、デフォルトではオーバーヘッドの少ない “${JAVA_HOME}/lib/jfr/default.jfc” が指定されて実行される❏ “${JAVA_HOME}/lib/jfr/profile.jfc” を指定することで、より詳細な情報が取得できる。.jfc ファイルの記述を変更して取得情報のカスタマイズも可能。❏ ただし詳細な情報を取得しようとすると、実行時のオーバーヘッドは大きくなるので注意JFR でより詳細な情報を取得する
❏ 例えば “path-to-gc-roots” オプションは、profile.jfcを指定すると、リークの可能性があるオブジェクトに対して、割り当てられている場所からのスタックトレースを確認することができるJFR でより詳細な情報を取得するjava \-XX:StartFlightRecording=\dumponexit=true,\filename=./output/jdbc-bad-sample-FULLGC-profile.jfr,\disk=true,\path-to-gc-roots=true,\settings=profile \-Xms20M -Xmx20M -jar ./target/jdbc-bad-sample.jarJava Platform, Standard Edition Java Flight Recorderコマンド・リファレンスhttps://docs.oracle.com/javacomponents/jp/jmc-5-5/jfr-command-reference/diagnostic-command-reference.htm
JFR でより詳細な情報を取得する❏ 「ライブ・オブジェクトのサンプル」でリーク候補を選択すると、スタックトレースが表示されるようになるライブ・オブジェクトのサンプル:AbandonedConnectionCleanupThreadクラスに関するオブジェクトが赤文字で表示されているスタックトレース:Main Thread からjava.sql.DriverManager.getConnectionという記載があり、Connection 周りで参照されているオブジェクトにメモリリークの原因がありそうと推測できる
まとめ
❏ この発表では FullGC, OOME が発生するアプリケーションに対し、原因を突き止めるために JFR, JMC, MemoryAnalyzer などのツールを用いた方法をご紹介した❏ 参考文献、まとまった書籍などがなかなかない分野と感じているので、皆様も是非情報発信していただけると嬉しいです!まとめ
❏ Java Platform, Standard Editionトラブルシューティング・ガイド❏ https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/toc.html❏ Oracle の公式ドキュメント❏ 様々なツールの使い方や、役立つオプションなどの情報が豊富❏ Javaパフォーマンス❏ https://www.amazon.co.jp/dp/4873117186/❏ Java アプリケーションを運用する上でのノウハウが詰まっているオススメの参考文献https://www.oreilly.co.jp/books/images/picture_large978-4-87311-718-8.jpeg
ご清聴ありがとうございました