Javaの起動オプションにGCログ設定とヒープダンプを設定する

Java

Javaを用いたプログラムで、運用開始後に原因不明の障害が発生する場合は多々あります。


原因不明のメモリリーク、CPUの一時的な極端な上昇といった現象は、アプリケーションのログからはなかなか原因が突き止められない場合があります。
こういった事象に対応するために、GCログの出力と、OutOfMemory発生時のヒープダンプ出力をあらかじめ仕込んでおくと、原因の早期発見が期待できます。


今回は、Javaアプリケーションの起動設定について、GCログとヒープダンプの出力設定をおこなう方法について紹介します。


環境情報


  • Java 1.8.0_281

GCログの出力設定


GCログの出力設定は、Javaの起動オプションとして設定します。
設定オプションは以下になります。


 

-verbose:gc

GCログを出力する。

-Xloggc

GCログの出力先を設定する。

例)-Xloggc:./gc.log-%t

 

ファイル名に設定できる変数要素としては以下の2つがあります。

%t=ログファイルを作成した日時

%p=JavaプロセスのID

-XX:+UseGCLogFileRotation

GCログのローテーションをおこないます。

-XX:+PrintGCDateStamps

GCログに時間のタイムスタンプを表示します。

-XX:+PrintGCDetails

GCログの詳細を出力します。

-XX:NumberOfGCLogFiles

GCログのローテーションをおこなう際、ローテーションするファイルの数を定義します。

-XX:GCLogFileSize

1つのログファイルのファイルサイズを定義します。

定義したファイルサイズに到達したタイミングで、ローテーションがおこなわれます。

定義したファイルサイズに到達したタイミングで、ローテーションがおこなわれます。

 

M=メガ

k=キロ(最小は7K

 


設定例としては以下になります。
実行するプログラムは「GcSample」となり、10キロのGCログファイルを最大5ファイルでローテーションをおこなっています。
GCログファイルにはログファイルを出力した日時を設定するために「%t」オプションを定義していますが、以下はWindowsのバッチファイル例なので、”%%”と記載してエスケープしています。


java -Xms256m -Xmx256m -verbose:gc -Xloggc:./gc.log-%%t -XX:+UseGCLogFileRotation -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10k GcSample 

この定義でログファイルを出力した場合は、以下のような定義となります。


gc.log-2021-02-11_15-52-26.0
gc.log-2021-02-11_15-52-26.1
gc.log-2021-02-11_15-52-26.2
gc.log-2021-02-11_15-52-26.3
gc.log-2021-02-11_15-52-26.4.current ← 出力中ファイル

5ファイルでローテーションされています。


出力されたファイルはGCViewerなどのビューアで参照するのが一般的になります。


ヒープダンプ(HeadDump)の出力設定


GCログを設定しておくことも大事なのですが、GCログだけではOutOfMemoryが発生した場合の、リーク箇所などについては詳細を判断することはできません。

ヒープダンプを設定しておくと、OutOfMemory発生時にヒープダンプ(HeadDump)が出力されるので、詳細の原因調査が可能となります。


-XX:+HeapDumpOnOutOfMemoryError

OutOfMemoryが発生した場合にヒープダンプを出力します。

-XX:HeapDumpPath

ヒープダンプの出力先フォルダ、および、ファイル名を指定します。

ファイル名を指定しない場合は、「java_pidxxx.hprof」といったファイル名になります。

xxxはプロセスIDです。

-XX:OnOutOfMemoryError

OutOfMemoryが発生した場合に実行するコマンドを定義することができます。

実行するコマンドは、シングルクォーテーションで囲んで定義します。

 

例)-XX:OnOutOfMemoryError=”cmd string.”


OutOfMemoryが発生してヒープダンプを出力した際、指定したフォルダにダンプファイルを出力するのですが、同名ファイルが存在する場合は上書きしてくれません。
つまり、同じプロセスIDのJavaプロセスで複数回のOutOfMemoryが発生した場合、一番最初のヒープダンプだけが残ります。


一番最初のヒープダンプを残すのではなく、一番最後のヒープダンプを残したい場合は、OnOutOfMemoryErrorオプションを使います。
以下の例では、ヒープダンプが発生したタイミングで出力したダンプファイルをバックアップフォルダに移動をおこないます。
この定義をおこなうことで、ヒープダンプ出力先フォルダにはダンプファイルは残らず、バックアップフォルダに最後のヒープダンプが残ることになります。


java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ -XX:OnOutOfMemoryError="D:/gc/gc-move.bat" GcSample 

OnOutOfMemoryErrorオプションに定義したgc-move.batの中身は以下になります。
backというフォルダに、出力したヒープダンプファイルを強制上書きコピーしています。

move /Y java*.hprof ./back/

OnOutOfMemoryErrorオプションに定義したコマンドは、ヒープダンプが出力された後に実行されるようです。


①:OutOfMemoryが発生

②:ヒープダンプを出力

③:OnOutOfMemoryErrorオプションに定義したコマンドを実行


このような動きであることを考慮して、OnOutOfMemoryErrorオプションに定義したコマンドを検討する必要があります。


仮に、最初のヒープダンプファイル・最後のヒープダンプファイル、という事ではなく、ヒープダンプの履歴を取りたい(全てのヒープダンプファイルを残しておきたい)場合は、上記例の「gc-move.bat」を修正すれば、様々な定義をおこなうことができます。
しかし、ヒープダンプファイルはプロセスのヒープ情報をそのままダンプしているので、かなりサイズが大きいファイルとなります。
そのため、ダンプファイルの履歴をとることはおすすめはしません。



ロリポップレンタルサーバで、クーロンを使ってバッチを実行し、必ず結果をメールで受信

JavaScript

今回は、ロリポップレンタルサーバでクーロン設定をおこなう方法、および、シェル実行結果をメールで受け取る方法について紹介します。


ロリポップは、crontabを直接編集してクーロンの設定をおこなうことはできないですが、ユーザ専用ページのクーロン設定画面で細かい起動設定をおこなう事ができます。
このクーロン設定画面では実行結果を受け取るメールアドレスの設定も可能であり、実行結果のメール受信が可能です。


メールでの受信は、バッチの実行結果によってメール本文の内容を変更する方法についても紹介していきます。


環境情報


バッチから実行するバッチは、Pythonで実装されていることを前提とします。


  • レンタルサーバ:ロリポップ
  • シェルから呼び出すバッチ:Python

WEB画面でクーロン設定をおこなう


ロリポップでのクーロン設定は管理コンソールのWEB画面でおこないます。


ロリポップのユーザー専用ページにログインし、左メニューで「サーバーの管理・設定」から「cron設定」を選択します。


ロリポップでのクーロン設定

cron設定画面がひらきます。
クーロンの設定、および、設定済クーロンの確認は、本画面でおこないます。



画面上にあるように、日付(月)・日付(日)・曜日・時間(時)・時間(分)をプルダウンで形式で選択する形で設定をおこないます。
筆者の契約しているプランはエコノミープランですが、以下の設定となります。
プランによって異なるのは最小実行間隔のみ(1分毎、5分毎)となります。


日付(月)

毎月、1月~12月

日付(日)

毎日、1日~31日

曜日

毎日、日曜日~土曜日

時間(時)

毎時、0時~23時

時間(分)

1分毎~10分毎、0分~59分


このように設定項目は豊富なので、ほとんどの設定のニーズに対応することができます。
しかし、crontabを直接編集するような設定はおこなうことはできません。
例えば、毎週、月曜日と金曜日だけ実行するという設定は、1つの設定ではおこなうことができないということです。
毎週、月曜日と金曜日に実行するといった設定をおこなう場合は、月曜日に実行するという設定と、金曜日に実行するという設定の2つの設定が必要となります。


複数の設定をおこなえばcrontabを直接編集することと同じ事ができるのであれば問題ないと思われますが、クーロンの設定には上限があるので注意が必要です。
クーロンの設定上限は、契約しているプランによって異なります。


 

cron登録数

最小実行間隔

エコノミー

1

5分毎

ライト

5

5分毎

スタンダード

10

1分毎

ハイスピード

10

1分毎

エンタープライズ

10

1分毎


メールでシェル実行結果を受け取る


クーロンの実行結果をメールで受け取ることができます。
設定は簡単で、メールアドレスをWEB画面で設定するだけです。



この設定をおこなうだけで、クーロンの実行結果が登録したアドレス宛にメールが送信されます。


しかし、メールが送信される条件は、クーロンの実行結果に出力がある場合、のみとなります。
つまり、標準出力やエラー出力といった出力がおこなわれない場合は、メールは送信されません。


メールでバッチが動作した結果を受信するためには、バッチを実行するシェルファイルに標準出力をおこなうように設定をおこなえばよいです。


#!/bin/bash
python cronSample.py
echo "バッチ処理が終了しました。" >&1

WEB画面で設定したメールアドレスには、標準出力した”クーロン設定での処理完了”とだけ記載されたメールが送信されます。


上記の設定では、クーロンが実行されたタイミングで必ずメールが送信されます。
本来であれば、クーロンの実行が失敗した時、といった一定の条件と一致した場合についてのみメールを送信したい場合がほとんどです。


その場合は、プログラムでの実行結果をシェルで受け取り、受け取った実行結果によって標準出力有無を判断する必要があります。


#!/bin/bash
ret=$(python cronSample.py)
if [ $ret = 0 ]; then
  echo "バッチ処理で異常が発生しました。" >&1
fi
#!/usr/local/bin/python3.4
# coding: utf-8
import sys

sys.stdout.write("0")

シェルの中身はシンプルで、Pythonで実行した結果が失敗(0)であった場合に標準出力をおこなっています。
結果、失敗したらメールが送信される、ということになります。


Pythonのプログラムでは、0を標準出力しているのみです。
これはサンプルプログラムでなので0を標準出力しているのみなのですが、実際のプラグラムでは、この部分を修正すればよいです。