Java | Java 詳細・モダン文法:JVM・パフォーマンス – Stop The World

Java Java
スポンサーリンク

Stop The World を一言でいうと

Stop The World(STW)は、
JVM が何か重要な作業をするために、アプリケーションのスレッドを一時的に全部止めること」です。

特に GC(ガーベジコレクション)のときによく出てくる言葉で、
「GC のこのフェーズは STW です」と言われたら、
「その間、アプリ側の処理は止まっている」と理解して大丈夫です。

あなたが書いたコードが悪くなくても、
JVM の都合で一瞬世界が止まる——それが Stop The World です。


なぜ「世界を止める」必要があるのか

メモリを安全に触るには「動いている手」を止める必要がある

GC は、「どのオブジェクトがまだ生きているか」を調べるために、
ヒープ上のオブジェクトや参照をスキャンします。

もしその最中に、アプリケーションのスレッドが
「この参照を別のオブジェクトに差し替えよう」
「このフィールドを null にしよう」
とメモリを書き換えたらどうなるでしょう。

GC が見ている途中で参照関係が変わってしまうと、

本当は生きているオブジェクトを「死んでいる」と誤判定して捨ててしまう。
逆に、本当はもう使われないオブジェクトを「生きている」と勘違いして残してしまう。

といった致命的なバグにつながります。

これを防ぐために、
「今からメモリの状態をじっくり調べるから、その間は誰も触らないで」
という時間が必要になります。

それが Stop The World です。

例えるなら「棚卸しのために店を一時閉店する」

お店で在庫の棚卸しをするとき、
お客さんが商品を手に取って移動させていたら、
正しい数を数えられません。

だから「棚卸し中は一時閉店します」として、
お客さんの出入りを止めることがあります。

GC にとっての STW は、まさにこの「棚卸しのための一時閉店」です。
在庫(オブジェクト)の状態を正しく把握するために、
一瞬だけ世界を止める必要があるのです。


具体的に何が止まるのか

アプリケーションスレッドが全部止まる

STW 中は、アプリケーションが動かしているスレッド(リクエスト処理、バッチ処理、バックグラウンド処理など)が一時停止します。

あなたのコードでいうと、
for ループも、if も、DB アクセスも、その瞬間は一切進みません。

JVM が「止まってください」と合図を出し、
全スレッドが安全なポイントまで来たところで一斉に止められます。
その後、GC などの処理が終わると、再び一斉に再開されます。

止まっている時間はどれくらいか

STW の時間は、

ヒープのサイズ。
生きているオブジェクトの量。
使っている GC の種類。

などによって変わります。

数ミリ秒で終わることもあれば、
設定や状況が悪いと、数百ミリ秒〜秒単位になることもあります。

Web アプリで「たまにレスポンスが 1 秒固まる」
リアルタイム処理で「一瞬だけ妙に遅れる」
といった現象の裏に、STW が潜んでいることはよくあります。


簡単なイメージコードで「止まる」を感じてみる

ループがたまに「カクッ」と止まる

実際の GC のタイミングは制御できませんが、
イメージとしてこんなコードを考えてみてください。

public class StwImage {
    public static void main(String[] args) {
        // 別スレッドで大量にオブジェクトを作る
        new Thread(() -> {
            while (true) {
                byte[] data = new byte[1024 * 1024]; // 1MB
            }
        }).start();

        // メインスレッドで時間を測り続ける
        long last = System.currentTimeMillis();
        while (true) {
            long now = System.currentTimeMillis();
            long diff = now - last;
            if (diff > 100) {
                System.out.println("一瞬止まったかも: " + diff + " ms");
            }
            last = now;
        }
    }
}
Java

このコードは、
片方のスレッドでひたすら 1MB の配列を作り続け、
もう片方のスレッドで「前回から何ミリ秒経ったか」を測っています。

GC が頻繁に走るような状況になると、
メインスレッドも STW で止められるので、
diff が普段より大きくなり、「一瞬止まったかも」と表示されることがあります。

これはあくまでイメージですが、
「自分のコードはループを回しているつもりなのに、
JVM の都合で一瞬止められることがある」
という感覚を持っておくと、STW を現実のものとして捉えやすくなります。


GC と STW の関係をもう少しだけ深掘りする

すべての GC が同じように止めるわけではない

古典的な GC(Serial GC など)は、
「GC のほぼすべてのフェーズが STW」でした。

最近の GC(G1, ZGC, Shenandoah など)は、
「できるだけアプリケーションと並行して GC を進め、
どうしても必要な部分だけ STW にする」
という工夫をしています。

それでも、

ルートのスキャン。
一部のマークやリロケーション。
メタデータの更新。

など、「ここだけは止めないと危ない」というポイントでは、
短時間の STW が発生します。

「止まる時間を短くする」ための GC

G1 GC などは、
「STW の時間を一定以下に抑えたい」というニーズに応えるために設計されています。

例えば、
「STW を 200ms 以下にしたい」といった目標を設定し、
その範囲で GC の仕事量を調整する、といったことができます。

ただし、
「STW がゼロになる」わけではありません。
「止まるけれど、止まる時間を短く・予測しやすくする」
という方向の進化です。


初心者として、Stop The World をどう意識すればいいか

まずは「そういう現象がある」と知っておく

最初の一歩は、
「Java の世界では、JVM の都合でアプリが一瞬止まることがある」
という事実を知っておくことです。

これを知らないと、

「コードは何も重いことをしていないのに、
なぜかたまにレスポンスが跳ねる」

という現象を、永遠にアプリ側のバグだと思い続けてしまいます。

そのうえで、コード側でできること

いきなり GC の細かいチューニングに走る必要はありません。
まずは、コード側で次のようなことを意識するだけで十分です。

短命オブジェクトを無駄に作りすぎない。
長生きする大きなオブジェクトを、不要なのに参照し続けない。
巨大な一括処理ではなく、適度に分割して処理する。

これだけでも、
GC の負荷が下がり、STW の頻度や時間がマイルドになります。


まとめ:Stop The World を自分の言葉で説明するなら

あなたの言葉で整理すると、こうなります。

「Stop The World は、JVM が GC などの重要な処理を安全に行うために、
アプリケーションのスレッドを一時的に全部止める現象。
メモリの参照関係を正しく調べるには、その間アプリがメモリを書き換えないようにする必要があるから。

この“世界が止まっている時間”が長くなると、
レスポンスの一時的な悪化や、リアルタイム性の低下として表に出る。
最近の GC はこの時間を短くする工夫をしているが、ゼロにはならない。

だから、
『JVM の都合で世界が一瞬止まることがある』という前提を受け入れたうえで、
オブジェクトの作り方やライフサイクルを意識し、
GC に優しいコードを書くことが、パフォーマンスの第一歩になる。」

タイトルとURLをコピーしました