メモリが少ない環境でのJava8使用の注意点
諸般の都合でどうしてもメモリが潤沢ではない環境でのテストを行わなければならない場合があるかと思います。
また、Java7もすでにEOLということでJava8の開発も多くなっているかと思いますが、Java8になって初めて遭遇した問題がありました。
そんなわけでJava8で開発を始めた頃のお話です。
TomcatアプリがJava7以前では重いながらも動いていたのに、Java8ではスワップの重力を乗り越えたと思ったら急にアプリが応答しなくなってしまい、よく見たらTomcat(java)プロセスがいません。
アプリケーションやTomcatのログを見てもOutOfMemoryErrorが出ているわけでもなく、仕方なくTomcatを起動しなおしてテスト再開するとまたプロセスがふっと消えている…。
そんな怪談話みたいなことがあるかと思いながら/var/log/messagesを見ると
Sep 17 01:23:54 dev01 kernel: Out of memory: Kill process 18657 (java) score 140 or sacrifice child Sep 17 01:23:54 dev01 kernel: Killed process 18657, UID 91, (java) total-vm:1894428kB, anon-rss:629252kB, file-rss:6408kB
はい、OOM Killer先生でした。
Java8になってメモリの構成が変わったため、従来のJVMパラメータ指定だけでは不十分だったようです。
具体的にはXX:CompressedClassSpaceSizeのデフォルト値が1GBであることが主原因です。 (CompressedClassSpaceは64ビット環境でポインタを圧縮する(CompressedOops)際に使われる領域)
メモリが1GB程度の環境では容易にOOM Killerの対象になるでしょう。
本来はjstatを見ながら最適値を見つけた方がいいですが、OOM Killerが発動するような環境であれば-XX:CompressedClassSpaceSize=32mあたりから始めてみてはどうでしょうか。
そもそも64bitJVMを使ったものか、という話かもしれません。 プロダクト環境でもメモリは4GBであれば32bitJVMの方がリソースに優しいですね。
ちなみに-XX:MetaspaceSizeは逆にデフォルト値が小さめなので、どうせすぐ拡張されるくらいならもう少し大きくしておいた方がよさそうです。