Java
Bash
Linux

Javaのリソース取得 ~高負荷・長時間化・フリーズ対応~

More than 1 year has passed since last update.

はじめに

取得すべき情報の調査方法までは言及しません。
個人的な備忘録を兼ねて、Javaが高負荷・長時間化・フリーズしたという時に
取得したい情報についてまとめます。
※ここでいう長時間化は「処理は止まっていないがSLAを守れない速度で処理し続けている」ことを表す。

そもそもの発端

Javaが長時間化・フリーズした場合、自分(もしくはチーム)で調査するか
どこか別のチームに調査依頼するか行うと思いますが、

「○○が長時間化しました!調査お願いします!情報は無いよ!」
「(情報の取り方わからないし、何取ればいいかもわからない)とりあえずkillした」

ということが時たまあります。
これでは聞かれた側も調査できません。
場合によっては
「情報が無いから調査無理、再現待ち」
となるか
「情報取るためにとりあえずもう一回動かそう」
となります。
それは避けたいので発生したときの情報を取得するか、取得してもらいましょう。

ただし、高負荷が発生しているときは別です。
放置するとシステム全体に悪影響を与える可能性があるため、情報の取得をすぐにできない
場合は一旦諦めて高負荷のプロセスをkillする等の対応を優先する必要があります。

プロセスIDの特定

情報を取得するためにはJavaアプリのプロセスIDを特定する必要があります。

【高負荷の場合】
psコマンドで特定してもいいですが、topコマンドの方が視覚的にわかりやすいです。
topコマンドではパフォーマンス状態をモニタリングすることができます。
高負荷が発生しているサーバにログインしてからtopコマンドを実行。
※高負荷過ぎてsshで接続できない場合は接続を何回か試してください。
top -c
topコマンド実行後、下記コマンドを入力すると高負荷のプロセスIDを特定できます。
「Shift+m」:メモリ使用率順にソート
「Shift+p」:CPU使用率順にソート

【長時間化・フリーズの場合】
psコマンドでプロセスIDを特定できます。
Javaアプリをバッチ起動している場合はそのコマンド文(スクリプト文)から
プロセスIDを特定することができます。
ps -ef | grep "コマンド文"
上記コマンドでコマンド文のプロセスIDが取得できるため、そのコマンド文が
親プロセスIDであるプロセスのプロセスIDを取得し、さらにそのプロセスIDが
親プロセスIDであるプロセスを取得し……を繰り返せばJavaアプリのプロセスIDを
特定できます。
(もっとスマートな方法があるかもしれません……)

取得したい情報の取得

若干過剰気味に記載しています。
リダイレクトすればファイルに出力することが可能です。

【ディスク情報の取得】
df -k

【生存しているオブジェクトのみのヒープダンプの取得】
jmap -histo:live ${PID}

【全てのヒープダンプの取得】
jmap -dump:format=b,file=${OUT_PATH}/jmap_heap-dump.dat ${PID}

【スレッドダンプの取得】
jstack -l ${PID}

【パフォーマンス状態の取得】
top -bc -n 1

【プロセス情報の取得】
ps -ef |grep ${PID}

【GC状態の取得】
jstat -gcutil ${PID} 10000 5

【ネットワーク接続状態の取得】
netstat

【プロセスが利用しているポートの取得】
lsof

【CPU情報の取得】
cat /proc/cpuinfo

【メモリ情報の取得】
cat /proc/meminfo

さいごに

発端に軽く記載しましたがこの記事で伝えたかったのはリソースの取得方法よりも、
問題が発生した事象だけ伝えて丸投げするのはやめましょう、ということです。
調査を依頼するときは自分が言われて調査できるだけの内容を添付した上で
依頼することを勧めます。