How to Examine Memory Usage of JVM
JVM の、主にメモリ消費の様子を知りたい。方法をいくつか調べたのでメモしておく。
今のところ次のような使い分けになりそう。
- その場で様子を見るなら VisualVM
- ログを取っておいて後から見るなら GCViewer
- 何がどのくらいメモリを消費しているか調べるなら Eclipse Memory Analyzer
jmap
JDK 付属の jmap を jmap -heap <pid>
のように実行すると JVM の各メモリ領域の使用量が分かる。
jcmd
JDK 付属の jcmd を jcmd <pid> GC.class_histogram
のように実行すると、どのクラスのインスタンスがどのくらいメモリを使っているのかが分かる。ただし #bytes
に各インスタンスが参照している先は含まれていない(あるクラス Klass
が巨大な java.lang.String
を参照していたとしても、それは Klass
の #bytes
には反映されない)ようなので、解釈には注意が必要と思われる。
jstat
JDK 付属の jstat を jstat -gc <pid>
のように実行してガベージコレクトされたヒープの統計データ を見たり jstat -gccause <pid>
のように実行して直前や現在のガベージコレクションの原因 を見たりできる。
jvmtop
jvmtop を使うと top を使うような感じで JVM の様子を見ることができる。たとえば GC に費された時間の割合やヒープの使用量などが分かる。各数値の見方はドキュメントに書いてある。
VisualVM
VisualVM を使うと GUI で JVM の様子を見ることができる。グラフで表示してくれるので、時系列を見たいときは jvmtop より便利そう。
GCViewer
GCViewer を使うと GC のログをグラフで表示することができる。
GC のログは java のオプションに -Xloggc:ログのパス -XX:+PrintGCDetails -XX:+PrintGCDateStamps
あたりを指定して取れば良い。
Eclipse Memory Analyzer
Eclipse Memory Analyzer を使うと GUI でヒープダンプを解析できる。とくに dominator_tree を見ると何にメモリを取られているかが分かりやすい。
ヒープダンプは JDK 付属の jcmd で jcmd <pid> GC.heap_dump /path/to/dump.hprof
のようにして取れる。あるいは java の起動オプションで -XX:+HeapDumpBeforeFullGC
や -XX:+HeapDumpAfterFullGC
をつけるとフル GC の直前や直後にヒープダンプを取れる。ファイルはカレントディレクトリに java_pidNNNNN.hprof
という名前(NNNNN
は pid)で作られるらしい。複数作られるときは java_pidNNNNN.hprof
, java_pidNNNNN.hprof.1
, java_pidNNNNN.hprof.2
のように連番になる。
ところで Eclipse Memory Analyzer 自体の起動オプションには -Xmx1024m
が指定されており(OS X の場合 MemoryAnalyzer.app/Contents/MacOS/MemoryAnalyzer.ini にある)少ないので、巨大なヒープダンプを解析したい場合は適当に増やした方が良い。