その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Web Application Server を動かす時の Java8 起動オプションのメモ

一般的な Web Application Server *1 を Java8 で動かすにあたって,最近有効にしている起動オプションについてメモ.
何か間違っていたり,あるいは「こっちの方が良い」みたいなのがあれば教えて下さい.

-server

server mode で起動させる (指定しないと client mode になる可能性がある,マシンスペックによってスイッチする?).

-Djava.net.preferIPv4Stack=true

If IPv6 is available on the operating system the underlying native socket will be an IPv6 socket. This allows Java(tm) applications to connect too, and accept connections from, both IPv4 and IPv6 hosts.
If an application has a preference to only use IPv4 sockets then this property can be set to true. The implication is that the application will not be able to communicate with IPv6 hosts.
https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html

IPv6 が使える OS では IPv6IPv4 両方のホストと接続できるが,true にすることで IPv4 ソケットだけを使うようにする.

-Dnetworkaddress.cache.ttl=0
networkaddress.cache.ttl=0

Specified in java.security to indicate the caching policy for successful name lookups from the name service.. The value is specified as integer to indicate the number of seconds to cache the successful lookup.
A value of -1 indicates "cache forever". The default behavior is to cache forever when a security manager is installed, and to cache for an implementation specific period of time, when a security manager is not installed.
https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html

デフォルトでは DNS の成功結果を未来永劫キャッシュするようになっていてアレなので,0を設定することでキャッシュしないようにする.

[追記]
id:astjさんに教えてもらったんですが,このオプションはセキュリティポリシーのため -D では有効になりません.

セキュリティーポリシーの一部であるため、-D オプションや System.setProperty() API では設定されません。その代わり、これらのプロパティーは JRE のセキュリティーポリシーファイル lib/security/java.security で設定されます。
https://docs.oracle.com/javase/jp/7/api/java/net/doc-files/net-properties.html

すみませんすみません.

-Dnetworkaddress.cache.negative.ttl=0
networkaddress.cache.negative.ttl=0

Specified in java.security to indicate the caching policy for un-successful name lookups from the name service.. The value is specified as integer to indicate the number of seconds to cache the failure for un-successful lookups.
A value of 0 indicates "never cache". A value of -1 indicates "cache forever".
https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html

デフォルトでは DNS の失敗結果を 10 秒キャッシュするようになっている.0をセットすることでキャッシュしないようにする.

[追記]
上と同じ理由で-Dでは有効にならないのでセキュリティポリシーで設定してください.すみませんすみません.

-XX:OnOutOfMemoryError="kill -9 %p"

OutOfMemory が発生した時に実行するコマンドを設定できる.OOM が起きたら確実に自殺してもらう必要があるので kill を発行.事と次第によっては自動でアプリケーションを再起動するようにしても良いかもしれない.
Ref: OutOfMemoryErrorが発生したときにきちんとJavaプロセスを殺す - nekop's blog

[追記]
@さんに教えていただきましたが,Java8u92から追加された-XX:+ExitOnOutOfMemoryErrorを利用したほうが良さそうです (XX:OnOutOfMemoryErrorに指定したコマンドが間違っていてうっかり死なない,みたいなミスを回避できる).

-XX:+OptimizeStringConcat

Optimize String concatenation operations where possible
http://www.oracle.com/technetwork/articles/java/vmoptions-jsp-140102.html

文字列連結が最適化されるとのこと (Java 8 ではデフォルト true っぽい).

-verbose:gc
-Xloggc:/var/log/gc/gc.log.`date +%Y%m%d%H%M%S`

gc log を吐くようにする.またそれをどこに吐くかの設定.
log ファイル名のうしろに日付情報を付与することでlogが上書きされるのを防いでいる.

-XX:+UseGCLogFileRotation

gc log を1つのファイルに延々吐き続けるのではなく,ログローテーションを有効にする.
後述の XX:NumberOfGCLogFiles と XX:GCLogFileSize に関係がある

-XX:NumberOfGCLogFiles=5

ログローテーションさせるときに,最大何個のログファイルでローテーションさせるかの設定.数字 (ファイル個数) は任意.

-XX:GCLogFileSize=1024MB

gc log 1個あたりの最大容量 .容量は任意.

-Xms 2G

ヒープ領域の初期値.値については各アプリケーションごとにチューニングの余地がある.

-Xmx 2G

ヒープ領域の最大値.値については各アプリケーションごとにチューニングの余地がある.

-Xmn 512m

New 世代領域サイズ.値については各アプリケーションごとにチューニングの余地がある.

-Xss 256K

スレッドスタックサイズ.値については各アプリケーションごとにチューニングの余地がある.

-XX:MaxMetaspaceSize=128M

!!! これめっちゃ重要 !!!
Java 8以降だと,かつての「-XX:MaxPermSize」の代わりに設定してやる必要がある.
Permanent 領域は最大容量が有限だったのに対し,Java 8 以降の Metaspace 領域はメモリの許す限りモリモリ消費できてしまうので,最大容量を指定してキャップすることで安全側に倒してやる必要がある.
Ref: http://equj65.net/tech/java8hotspot/

-XX:+UseConcMarkSweepGC

Use concurrent mark-sweep collection for the old generation

CMS GC を使う.
Java のバージョンによってデフォルトのGCが変わりそうなので,設定したほうが安全な感じがした.

-XX:+CMSParallelRemarkEnabled

メジャーGCのRemarkフェイズをマルチスレッドで実行
(「動作させるマシンのCPUが2個以上かつ物理メモリが2Gbytes以上の場合には、自動設定される」とあるが念のため: http://www.atmarkit.co.jp/ait/articles/0704/24/news136_2.html)

-XX:+UseParNewGC

マイナーGCをマルチスレッドで実行
(「動作させるマシンのCPUが2個以上かつ物理メモリが2Gbytes以上の場合には、自動設定される」とあるが念のため: http://www.atmarkit.co.jp/ait/articles/0704/24/news136_2.html)

-XX:+UseCMSInitiatingOccupancyOnly

CMSが開始されるトリガーは二つある。ひとつはOld領域の利用率がCMSInitiatingOccupancyFraction に到達した場合。もうひとつは今CMS走らせないと先にヒープ埋まっちゃうよね、という統計判断を元にしたトリガー。UseCMSInitiatingOccupancyOnlyを付与すると後者のトリガーが無効になる。
http://nekop.hatenablog.com/entry/20140327/1395886237

というわけで,統計判断を無効に.

-XX:CMSInitiatingOccupancyFraction=75

XX:+UseCMSInitiatingOccupancyOnly と関連.

これらのデフォルトはMinHeapFreeRatio=40、CMSTriggerRatio=80なので、CMSInitiatingOccupancyFractionのデフォルト値は92になる。つまり、Old領域が92%になったときに最初のCMS GCが行われる。オブジェクトアロケーションが激しいようなアプリケーションでは92%だと手遅れになることがあるのでCMSInitiatingOccupancyFractionは下げたほうが良い。70とか。
http://nekop.hatenablog.com/entry/20140327/1395886237

[追記]

XX:CMSInitiatingPermOccupancyFraction=percent
Sets the percentage of the permanent generation occupancy (0 to 100) at which to start a GC. This option was deprecated in JDK 8 with no replacement.
http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

JDK8 以降は非推奨とのこと……JDK 9と出会ったらまた考えましょう

[追記]
@さんに教えていただきましたが,これはオプション自体が非推奨なわけではなく,JDK 9ではCMS GC自体が非推奨のためこのような記述になっているとのこと.

-XX:+ScavengeBeforeFullGC

FullGC 走らせる前に young の GC を走らせる

-XX:+CMSScavengeBeforeRemark

If turned on, it will cause a minor collection to occur just before the remark. That's good because it will reduce the remark pause. That's bad because there is a minor collection pause followed immediately by the remark pause which looks like 1 big fat pause.
https://blogs.oracle.com/jonthecollector/entry/did_you_know

CMS の Remark 処理の前にマイナーの scavenge をするということらしい.

-XX:+TieredCompilation

階層型コンパイルを有効にする.有効にすると,コンパイル済コードの実行中に JVM が最適化のための統計情報を収集可能にする.

この改良の有効性はそれほど高くはないと思われる。サーバのスタートアップ時の処理時間は,デスクトップやアプレットベースのアプリケーションほど重要ではないからだ。ただし非常にダイナミックなシステムにおいて各ノードがすばやく立ち上がる必要のある場合などには,この変更でウォームアップ時間を短縮できるかも知れない。
http://www.infoq.com/jp/news/2011/11/java7-u1

とのことだが……

-XX:+UseCompressedOops

Enables the use of compressed pointers (object references represented as 32 bit offsets instead of 64-bit pointers) for optimized 64-bit performance with Java heap sizes less than 32gb.

XX:+UseCompressedOops オプションを使用すると、Java オブジェクトヒープのサイズが 32 ギガバイト未満の場合に、64 ビット JRE のパフォーマンスを向上させることができます。この場合、HotSpot はオブジェクト参照を 32 ビットに圧縮して、処理する必要のあるデータの量を減らします。

Ref: http://itdoc.hitachi.co.jp/manuals/link/has_v101000/0342020D/0766.HTM
Ref: http://www.oracle.com/technetwork/jp/articles/java/compressedoops-427542-ja.html
Ref: http://d.hatena.ne.jp/quitada/20120130/p1

-XX:+PrintGCDetails

gc log に詳細な情報を吐き出すようにする.GC algorithm や JVM のバージョンによって追加される情報は変わってくるようだが,とりあえず GCスループットの情報が出たりする.

-XX:+PrintGCApplicationStoppedTime

Stop The World が起きた時,stop した時間を gc log に書き出すようにする.

-XX:+PrintGCDateStamps

gc log の各行の先頭に日時のスタンプを付与する (PrintGCTimeStamps にすると,JVM が起動してからの秒数が先頭に付与されるようになる.ヒトが読みにくい気がするので Date に)

-XX:+PrintTenuringDistribution

young領域からold領域への昇格(昇格?)に関するログを出力するためのオプション.

-XX:+HeapDumpOnOutOfMemoryError

OOM Error が起きた時に Heap Dump を吐き出すようにする.

雑感

Xms,Xmx,Xmn 及び Xss は割と勘と経験で設定している感じがあるので,改善の余地が残されている気がする.
以下の記事なんかを参考にするのが良いのか;

JVMのチューニング - ITエンジニアとして生きる
[調査]JVMのスタックサイズについて - Akira's Tech Notes
ガベージコレクタの仕組みを理解する:チューニングのためのJava VM講座(後編) - @IT

*1:一般的の定義が曖昧