テクノロジー

MMORPGをJVMベース環境で運用するんですか??

AAnonymous
4分で読めます

はじめに

最近かなりだれていました。もう一度、気を引き締める必要があります。ㅠㅠ かなり久しぶりのブログ更新です。

前回の ゲーム制作計画 では、MMORPGを最後の挑戦に置くと書きました。その後に一番よく浮かぶ反応は、たぶんこれです。Java? JVMでリアルタイムMMORPGを作って運用するんですか? それってごく小規模でしか成立しないのでは?

今回の文章で言いたいことは一つだけです。JVMで高いHzのサーバーティックが現実的な選択肢になった最大の理由は、GCの進化です。Nettyも重要ですし、Kotlinも重要です。Zone分割やAOIのようなMMO特有の設計も重要です。しかし、その前にまず見るべきなのは pause time です。

このジャンルで本当に問うべきものは平均性能ではなくGCです

昔のJavaを覚えている人なら、リアルタイムネットワークゲームで最初に心配するのはたいていGCだったはずです。より正確には、throughput ではなく tail latency です。平均が良くても、一度長い stop-the-world が入れば、そのティックはすぐに遅れ、入力の応答性や戦闘の感触も崩れます。

これはゲームサーバーでは特に致命的です。

  • 20Hz サーバーのティック予算は 50ms です。
  • 60Hz サーバーのティック予算は 16.6ms です。
  • 128Hz サーバーのティック予算は 7.8ms です。

ここでGC pause が数十ミリ秒単位で跳ねると、平均TPSが健全に見えても、プレイヤーはすぐに違和感を覚えます。rubber banding、入力遅延、戦闘判定のずれ、packet flush の遅れは、たいていこうした tail の問題から始まります。ですから、JVMでリアルタイムゲームが可能かという問いは、実際には JVMの平均性能は十分か よりも GCがどこまで予測可能な短さになったのか に近いのです。

最新のJVM GCは昔のJVM GCではありません

ここで状況は大きく変わりました。OpenJDKの流れを見ても変化は明確です。

  • ZGC は JDK 15 で production feature になりました。
  • Generational ZGC は JDK 21 に入りました。
  • JDK 23 では Generational ZGC が default mode になりました。
  • JDK 24 では non-generational ZGC が削除されました。
  • つまり Java 25 は generational-only ZGC 時代の最初のLTS と見なせます。
  • Shenandoah もすでに production GC であり、Java 25 では generational mode が product feature に昇格しました。

重要なのは、単にJVMオプションが一つ増えたことではありません。OpenJDKの説明では、Generational ZGC は若いオブジェクトをより頻繁に回収しつつ、allocation stall のリスク、必要な heap overhead、GC CPU overhead を下げる方向で設計されています。そして pause の特性は維持されます。JEP 439 は application pause が通常 1ms未満 に保たれると説明しています。

これは、Javaゲームサーバーを敬遠させていた GCがたまに世界を止める という古い前提が、もはや標準的な前提ではなくなったことを意味します。GCが消えたわけではありません。ただ、GCそのものがJVMを最初から候補外にする決定的な理由ではなくなったのです。

実運用の事例も同じ方向を示しています

特に興味深い例の一つが Netflix です。Netflix は JDK 21 以降で Generational ZGC をデフォルト側へ切り替え、同社の説明では、現在は重要なストリーミング動画サービスの半分以上が JDK 21 上の Generational ZGC で動いています。

さらに重要なのは結果です。Netflix は当初、ZGC は多少の throughput を犠牲にして latency を買う選択肢になるだろうと予想していました。しかし実際には、average latency も P99 latency も改善し、CPU utilization も同等かそれ以上の結果になりました。最悪ケースでは、non-generational ZGC が同一ワークロードで G1 より 36% 多くCPUを使っていたのに対し、Generational ZGC ではむしろ 10% 近いCPU改善になったと説明しています。

もちろんNetflixはゲーム会社ではありません。しかし、ここで見るべきなのはサービスの種類ではなく、pause と tail latency をどれだけ安定して扱えるかです。リアルタイムゲームサーバーも、結局は同じ問題を、より厳しい予算の中で解いているからです。

では、JVMでリアルタイムゲームは作れるのか

ここまで来ると、JVMでリアルタイムゲームは可能か という問いを一文で雑に語るのはあまり意味がありません。領域を分けて見る必要があります。

十分に現実的な領域 はすでにかなり広いです。

  • ティックベースのMMORPG
  • サンドボックス型のワールドサーバー
  • ロビー、マッチメイキング、状態同期バックエンド
  • 20~60Hz 程度のサーバーティックを必要とするマルチプレイヤーゲーム

代表的な大衆事例としては Minecraft Java Edition サーバー があります。この事実だけでも、Javaではリアルタイムのマルチプレイヤーワールドを動かせない、という断定はもう維持しにくいと思います。

一方で、より慎重さが必要な領域も残っています。

  • ティック予算が極端に厳しい 128Hz の競技系FPS
  • 極端に deterministic な latency が必要なワークロード
  • メモリの余裕がほとんどない環境
  • GCスレッドと application thread が飽和CPU上で競合するシステム

つまり言いたいのは、JVMなら何でもできるということではありません。ただ 多くのゲームサーバー領域で、JVMを候補外にしていた最大の理由だったGC pause問題はかなり緩和された ということです。

そしてその次にようやくMMO固有の設計が意味を持ちます

ここでは順番が大事です。私は zone tick、システム別 cadence、AOI ベースの同期が重要だと何度も書いてきました。今作っている mmorpg-service も実際にその構造を採っています。

  • 基本ループは 10Hz
  • World と Zone の設定は 1~15Hz の範囲で調整可能
  • Monster AI は状態ごとに別の cadence を使う
  • ドロップ消滅チェックや自動回復の再試行はもっと遅い間隔で回す
  • Netty の I/O thread は packet を受けるだけで、実際のゲームルールは zone loop が適用する
  • AOIManager が距離とイベントに応じて同期頻度を変える

ただし、これらはあくまで GCが先にある程度解決された後の設計 です。pause time がまだ数十ミリ秒単位で跳ねる環境なら、その上で zone tick を分けても AOI を細かくしても、土台そのものが揺れます。高いHzを支えられるようになった最大の理由はGCの進化であり、MMO設計はその上に積む第二レイヤーです。

結局、この文章の核心はこの一文です

いまJVMで高いHz処理が可能になった最大の背景は、GCの進化です。

以前は Java = stop-the-world が怖い という結びつきが強すぎました。今は low-pause GC がはるかに成熟し、Generational ZGC と最新の Shenandoah の流れまで含めれば、JVM はもうリアルタイムサーバーの議論から自然に外されるプラットフォームではありません。

その上に Kotlin と Netty を載せ、さらに zone loop、cadence、AOI といったMMO固有の設計を積むと、話は完全に変わります。これからの問いは JVMだから無理では? ではなく、自分たちの latency budget の中で GC と tick をどう設計するか であるべきです。

私はまさにその地点で、JVMベースのMMORPG運用は十分に現実的な選択肢になったと考えています。