こんにちは、tonocchoです。
私は2000年にIT業界の門を叩いてからずっとメインはJavaを利用していますが、最近のJavaにはほとんど触れていません。で、今どきのJavaの起動オプションってどんななってんのかな、と思ったので調べてみることにしました。
起動オプションって何?
起動オプションは、例えば -Xmxってつけると最大のヒープサイズを指定できる、みたいなやつです。
java -helpって打つと色々と出てきますね。この起動オプションをしっかり設定してあげると、Javaのプログラムがクラッシュしたときに調査できたりしますし、安定したりもします。
よく使っているオプション
と言ってもモダンなJava実行環境ではもう使わない、もしくは、サポートすらされていない、っていうのもあるかもしれませんが、こんな感じです。
- -Xms 最小のヒープサイズを設定する
- -Xmx 最大のヒープサイズを設定する
- -verbose:gc ガベージコレクションが発生したときの情報を出力する
- -Xloggc:<file> ガベージコレクションのログを指定する
- -Xss スレッドスタックサイズを決める
- -XX:+HeapDumpOnOutOfMemoryError OutOfMemoryErrorが出たときにヒープダンプというファイルを出力する
- -XX:HeapDumpPath=/path/to/directory ヒープダンプの出力先を設定する(ヒープダンプは普通に巨大になるので、異なるファイルシステムやドライブを割り当てておくといいと思います)
この辺が割と使いがちで、PermSizeとかその辺も使ったりします。
実際に起動してみる
まずは、わざとOutOfMemoryErrorを出すクラスを作ります。
1 2 3 4 5 6 7 8 9 10 | import java.util.*; public class Test { public static void main(String[] args) { List<String> strings = new ArrayList<>(); while(true) { strings.add("abcdefghijklmnopqrstuvwxyz" + new Date().toString()); } } } |
試しにコンパイルして実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | > javac Test.java > java -Xmx1m Test Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.LinkedHashMap.newNode(LinkedHashMap.java:256) at java.util.HashMap.putVal(HashMap.java:632) at java.util.HashMap.put(HashMap.java:613) at sun.util.resources.OpenListResourceBundle.loadLookup(OpenListResourceBundle.java:146) at sun.util.resources.OpenListResourceBundle.loadLookupTablesIfNecessary(OpenListResourceBundle.java:128) at sun.util.resources.OpenListResourceBundle.handleKeySet(OpenListResourceBundle.java:96) at java.util.ResourceBundle.containsKey(ResourceBundle.java:1824) at sun.util.locale.provider.LocaleResources.getTimeZoneNames(LocaleResources.java:263) at sun.util.locale.provider.TimeZoneNameProviderImpl.getDisplayNameArray(TimeZoneNameProviderImpl.java:124) at sun.util.locale.provider.TimeZoneNameProviderImpl.getDisplayName(TimeZoneNameProviderImpl.java:99) at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getName(TimeZoneNameUtility.java:240) at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getObject(TimeZoneNameUtility.java:198) at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getObject(TimeZoneNameUtility.java:184) at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObjectImpl(LocaleServiceProviderPool.java:281) at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObject(LocaleServiceProviderPool.java:265) at sun.util.locale.provider.TimeZoneNameUtility.retrieveDisplayNamesImpl(TimeZoneNameUtility.java:166) at sun.util.locale.provider.TimeZoneNameUtility.retrieveDisplayName(TimeZoneNameUtility.java:137) at java.util.TimeZone.getDisplayName(TimeZone.java:400) at java.util.Date.toString(Date.java:1045) at Test.main(Test.java:7)こ |
こんな感じでめでたく(?)OutOfMemoryErrorが出ました。ヒープサイズの最大値を1Mバイトにしています。
Java8で設定するとこんな感じ
java -Xmx1m -verbose:gc -Xloggc:./gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=. Test
こんな感じで実行すると、コマンドを実行したディレクトリにgc.logとjava_pid[process_id].hprofと言うファイルが生成されます。gc.logの中身を見るとわかりますが、必死にGCしてるけど全く減っていかない様子がわかります。
Java25だとどうなるの?
java -version
openjdk version “25-ea” 2025-09-16
OpenJDK Runtime Environment (build 25-ea+9-963)
OpenJDK 64-Bit Server VM (build 25-ea+9-963, mixed mode, sharing)
SDKmanでインストールできる多分最新のJDKを入れてみました。まずはそのまま実行
java -Xmx1m -verbose:gc -Xloggc:./gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=. Test
[0.000s][warning][gc] -Xloggc is deprecated. Will use -Xlog:gc:./gc.log instead.
Error occurred during initialization of VM
Too small maximum heap
…なるほど
java -Xmx10m -verbose:gc -Xlog:gc:./gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=. Test
こんな感じで実行したところ、GCのログが画面にも出力されましたが、どうやらgc.logも出力されました。
Heapdumpも出力されました。
java.lang.OutOfMemoryError: Java heap space
Dumping heap to ./java_pid124679.hprof …
Heap dump file created [13856480 bytes in 0.064 secs]
あんまそこまで変わってない気もしますが、色々できるようになっていそうです。
例えばこんな感じはどうでしょう
java -Xmx10m -verbose:gc -Xlog:gc*:./gc.log:tags,time,uptime,level:filecount=5,filesize=10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=. Test
こうすることで、GCログが1ファイル10Mバイトで5世代まで取得できます。
GCログやヒープダンプの解析方法
GCログやヒープダンプが出たとして、これらをどうやって見ればいいのでしょうか?ツールを使います。
GCログの解析:GCViewer
ネットを見たところ、こちらが評判いいようです。ためしに画像生成したらなんか怖い画像になりました。

多分張り付きっぱなしなんでしょうね。ただ、GCログを解析するのもいいんですが、これって結局どの時間帯からメモリリーク始まっていつクラッシュしましたっていうものでしかないので、「客観的に言うなお前が」って怒られるかもしれません。
ヒープダンプの解析:Eclipse Memory analyzer Tool
おっとここに来て久しぶりのEclipse、IntelliJのプロファイラはどうもUltimateでないと使えないぽいのと、Visual VMは最新のJavaだとセキュリティマネージャ周りで使えない?なんかすんなり行きませんでした。
Java8とか使っているのであれば、jvisualvmを起動するのも悪くないと思います。jhatはどうも不安定で好きになれません。
Eclipse Market PlaceからMemory analyzer toolとインストールして使ってみました。

まとめ
そんなわけで昔を思い出しながらちょっと書いてみましたが、Javaを実際に運用する上で、クラッシュしたときにどうするか、っていうのはやはり考えておいたほうがいいと思います。
そういうわけで久々の技術メモでした。