IT

Javaの起動オプション今と昔

tonoccho

こんにちは、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を出すクラスを作ります。

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());
    }
  }
}

試しにコンパイルして実行します。

> 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

https://github.com/chewiebug/GCViewer/wiki/Changelog

ネットを見たところ、こちらが評判いいようです。ためしに画像生成したらなんか怖い画像になりました。

多分張り付きっぱなしなんでしょうね。ただ、GCログを解析するのもいいんですが、これって結局どの時間帯からメモリリーク始まっていつクラッシュしましたっていうものでしかないので、「客観的に言うなお前が」って怒られるかもしれません。

ヒープダンプの解析:Eclipse Memory analyzer Tool

おっとここに来て久しぶりのEclipse、IntelliJのプロファイラはどうもUltimateでないと使えないぽいのと、Visual VMは最新のJavaだとセキュリティマネージャ周りで使えない?なんかすんなり行きませんでした。

Java8とか使っているのであれば、jvisualvmを起動するのも悪くないと思います。jhatはどうも不安定で好きになれません。

Eclipse Market PlaceからMemory analyzer toolとインストールして使ってみました。

まとめ

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

そういうわけで久々の技術メモでした。

著者について
殿内誠司・とのっちょ・tonoccho
殿内誠司・とのっちょ・tonoccho
サイト管理者・執筆者
2014年からニュージーランドに移住し、2023年にPermanent Resident VISAを取得、現在はWest Aucklandに在住し現地企業でプログラマーとして働いている。現地在住者ならではの情報発信をしたいと思いこのサイトを2024年9月に立ち上げた。趣味は家庭菜園、DIY、写真など。
Recommend
こちらの記事もどうぞ

ピアノの音をきれいに録音したいのでかなり調べたから共有してみます

tonoccho
とのっちょのNZ生活

Davinci Resolve + RODE Wireless PRO + デジカメで撮影した動画でタイムコード同期をしました

tonoccho
とのっちょのNZ生活

2025年の手帳を試作中

tonoccho
とのっちょのNZ生活

Dabvinci ResolveをPopOS+AMD GPUの環境でセットアップした備忘録

tonoccho
とのっちょのNZ生活

Dockerに入門する

tonoccho
とのっちょのNZ生活
記事URLをコピーしました