Linux環境でJavaアプリをsystemd管理している場合、 MemoryMaxの設定が原因で予期せぬOOMやプロセス強制終了が発生するケースが非常に多くあります。 本記事では、systemdのMemoryMaxとJavaメモリ(Heap / Metaspace / Direct Memory)の関係を整理し、 なぜ「Heapに余裕があるのに落ちる」のかを実務視点で解説します。
この記事でわかること
systemdのMemoryMaxが何を制御しているのか、Javaのどのメモリ領域が制限対象になるのかを理解できます。 また、実際に起きがちな障害パターンと、安全な設計・設定例まで把握できます。
systemd MemoryMaxとは何か
MemoryMax は systemd の unit ファイルで設定できる cgroup単位のメモリ上限です。 この値を超えた場合、カーネルはプロセスを OOM Kill します。
[Service]
MemoryMax=4G
重要なポイントは、MemoryMaxが制限するのは 「JVMヒープだけではない」という点です。
MemoryMaxが制限するメモリ範囲
MemoryMaxは以下をすべて合算して制限します。
- Java Heap(-Xmx)
- Metaspace(クラス情報)
- Direct Memory(ByteBuffer等)
- Thread Stack(スレッド数 × スタックサイズ)
- JNI / ネイティブライブラリ使用分
つまり、-XmxをMemoryMax未満にしても安全とは限りません。
よくある誤解①:XmxをMemoryMax以下にしている
以下のような設定は一見安全そうに見えます。
MemoryMax=4G
-Xmx3G
しかし実際の使用メモリは次のようになります。
- Heap:3GB
- Metaspace:200〜500MB
- Direct Memory:〜1GB(NIO使用時)
- Thread Stack:数百MB
結果としてMemoryMaxを超過し、cgroup OOMが発生します。
よくある誤解②:JavaのOOMログが出ない
MemoryMaxによるOOMはJVM外で発生するため、 以下の特徴があります。
- java.lang.OutOfMemoryError が出ない
- hs_err_pid*.log が出ない
- プロセスが突然 SIGKILL される
このため「原因不明の再起動」と誤認されがちです。
確認すべきログとコマンド
systemdステータス
systemctl status your-service
以下のような表示があれば MemoryMax が原因です。
code=killed, signal=KILL
Memory cgroup out of memory
カーネルログ
dmesg | grep -i memory
Javaメモリ設計の正しい考え方
MemoryMax を設定する場合、Heap以外を必ず考慮します。
安全な目安
MemoryMax = Xmx × 1.3〜1.5
例:
-Xmx4G
MemoryMax=6G
Direct Memoryの罠
Netty / Kafka / Elasticsearch などを使う場合、 Direct Memory が非常に大きくなります。
制御するには以下を明示します。
-XX:MaxDirectMemorySize=512m
これを設定しないと、MemoryMax超過の最大要因になります。
Metaspaceの管理
クラス動的ロードが多いアプリでは Metaspace が肥大化します。
-XX:MaxMetaspaceSize=256m
これにより予期せぬcgroup OOMを防げます。
推奨設定例(systemd + Java)
[Service]
MemoryMax=6G
ExecStart=/usr/bin/java \
-Xms4G -Xmx4G \
-XX:MaxMetaspaceSize=256m \
-XX:MaxDirectMemorySize=512m \
-jar app.jar
MemoryMaxを使うべきケース・使うべきでないケース
使うべき
- 複数サービス同居サーバー
- メモリ暴走防止が目的
使うべきでない
- 単一Javaサービス専用サーバー
- メモリを最大限使いたいバッチ処理
まとめ
systemd MemoryMax は強力だが非常に危険な制限です。 Javaのメモリ構造を理解せずに設定すると、 Heapに余裕があるのに突然死ぬという最悪の障害を引き起こします。
必ず「Heap + 非Heap + OS使用分」を意識した設計を行いましょう。



