Javaを何年も書いていると、言語よりもJVMの振る舞いで悩む場面が増えてきますよね。

なぜか速い、なぜか遅い、なぜかキャストで落ちる。原因はコードではなく、その下で動いている実行環境にあることが多いんです。今回は、コードの下でJVMが何をしているか——JVMの「気持ち」が分かるものを8つ集めました。1つでも知らなかったら儲けものです。

① メソッドは、小さいほど速くなることがあります

JITコンパイラの最重要の最適化がインライン化——メソッド呼び出しを、呼び出し先の中身で置き換える処理です。

これが効くと、呼び出しコストが消えるだけでなく、置き換えた先でさらに最適化が連鎖します。ただし条件があり、HotSpotはバイトコードが一定サイズ(既定で35バイト)以下の小さなメソッドを積極的にインライン化します。頻繁に呼ばれるメソッドはもう少し大きくても対象になりますが、巨大なメソッドは外れやすい。

つまり「1つの巨大メソッド」より「小さなメソッドの組み合わせ」の方が、読みやすいだけでなく、速いことがある。可読性と性能が同じ方向を向く、珍しい例なんです。

② newと書いても、オブジェクトが作られないことがあります

もう1つ、JITの魔法です。

メソッド内で作ったオブジェクトが外に漏れない(戻り値にもフィールドにも入らない)とJITが証明できると——エスケープ解析——そのオブジェクトはヒープに確保されず、フィールドが単なるローカル変数に分解されます(スカラー置換)。

「ループ内で毎回newしている、遅そう」というコードが、実測では速い理由がこれです。

「newは遅い」という古い常識は、JITが半分書き換えています。だから性能の話は、計測が先なんです。

③ 同じクラスなのに、ClassCastExceptionが出ます

「com.example.Userをcom.example.Userにキャストできない」——このエラー、実在します。

JVMにとってクラスの同一性は、「クラス名」ではなく「クラス名+読み込んだClassLoader」で決まります。同じjarを別々のClassLoaderが読めば、JVM内には同名の別クラスが2つ存在します。

アプリケーションサーバー、プラグイン機構、ホットリロード——複数のClassLoaderが絡む環境で必ず出会う怪現象です。

クラスは名前で決まらない。これを知っていると、調査の方向が一瞬で定まります。

④ 仮想スレッドは、100万本立てられます

Java 21で正式化された仮想スレッドは、OSスレッドと1対1ではない軽量スレッドです。

従来のスレッドは1本ごとに大きなスタックを持ち、数千本で苦しくなる。仮想スレッドはヒープ上の小さなオブジェクトなので、100万本規模でも動きます。I/O待ちの多いサーバー処理(1リクエスト1スレッドの素朴なモデル)が、そのまま高スループットになる。

ただし注意点もあり、Java 21ではsynchronizedブロック内で待つと、下のOSスレッドが固定されて(ピン留め)恩恵が薄れました。この点はJava 24で改善されています。

「非同期に書き直す」以外の選択肢が、Javaに戻ってきたんです。

⑤ Stringは、1文字1バイトで持っていることがあります

Java 9以降、Stringは内部表現を自動で切り替えます(Compact Strings)。

文字列がLatin-1の範囲(英数字など)に収まる場合は1文字1バイト、日本語などが含まれる場合は1文字2バイトのUTF-16で保持。以前は常に2バイトだったので、英数字中心のシステムではヒープの文字列領域が大きく減りました。

つまり同じ「10文字の文字列」でも、中身が英語か日本語かでメモリ使用量が違う。ヒープダンプで文字列が思ったより小さい/大きい理由は、これなんです。

⑥ GCの停止は、「止まれる場所」まで待ちます

Stop the Worldは、全スレッドがセーフポイントという「止まってよい地点」に到達して初めて始まります。

ここに罠があり、intカウンタの単純なループはJITの最適化でセーフポイントのチェックが省かれることがあります。すると、そのループが終わるまで他のスレッドは止まれず、GCが待たされる。最近のJVMでは対策が入って緩和されていますが、考え方として押さえておきたいところです。

「GCの停止時間は短いのに、実際のレスポンスの棘が長い」——このズレの正体は、停止そのものではなく「止まるまでの時間(Time To Safepoint)」だったりします。

GCログの数字と、ユーザーの体感がズレる理由の1つです。

⑦ コンテナの中のJVMは、メモリ上限を自分で読んでいます

コンテナ時代の必修知識です。

現代のJVMは、コンテナのメモリ制限(cgroup)を認識して、ヒープの最大値を自動で決めます。既定はコンテナメモリの25%(MaxRAMPercentage)。

つまり2GBのコンテナで何も指定しなければ、ヒープは512MB。「メモリを2GBも渡したのにOOMになる」の答えがこれです。逆に、-Xmxをコンテナ上限ぎりぎりに設定すると、ヒープ外の領域が確保できずコンテナごと強制終了されます。

JVMのメモリ設定は、ヒープ+ヒープ外+OS分の「合計」で考える。コンテナに入れた瞬間、この計算が必須になるんです。

⑧ ラムダは、匿名クラスではありません

「ラムダは匿名内部クラスの糖衣構文」——違います。

匿名クラスはコンパイル時に .classファイルが生成されますが、ラムダはinvokedynamicという仕組みで、初回実行時に生成されます。クラスファイルは増えず、キャプチャする変数がなければインスタンスも使い回される。

同じinvokedynamicは、Java 9以降の文字列連結(+)にも使われています。「StringBuilderに置き換わる」という昔の説明は、もう正確ではありません。

バイトコードの下で、Javaは静かに設計を変え続けているんです。

番外編:手元で確かめられる小ネタ

JITの判断を見る

java -XX:+PrintCompilation MyApp

# ①のインライン化の様子(診断オプション)
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining MyApp

どのメソッドがいつコンパイルされ、何がインライン化されたかが流れます。

Java Flight Recorder(JFR)は標準装備
本番でも使える低オーバーヘッドのプロファイラが、JDKに最初から入っています。⑥のセーフポイント待ちも、ここで見えます。

ZGCは、停止時間ミリ秒未満の世界
Java 15で正式化されたZGCは、ヒープが数百GBでも停止時間をミリ秒未満に抑える設計です。Java 21で世代別ZGCが入り、その後は世代別が標準になりました。GCの選択肢は、この10年で様変わりしているんです。

RJCでも、JVMと向き合っています

RJCが携わる金融業界向けのJava 21プロジェクトのような世界では、長時間稼働・ピーク性能・コンテナ運用が当たり前の前提です。⑥⑦のような「コードの外側」の知識が、そのまま安定稼働に効くんですよね。

社内のJava探求グループでも、こういう深いところを掘り下げています。Techチームで共有しながら開発しています。

よくいただくご質問

Q. まずどれから押さえるべきですか?
A. ⑦かなと思います。コンテナ運用は避けて通れず、設定ひとつで本番の生死が決まる話なんですよね。

Q. ここまでJVMを知る必要はありますか?
A. 日常のコードには不要ですが、「なぜか遅い」「なぜか落ちる」の調査で一段深く潜れる人は、チームに1人いるだけで価値が全然違うんです。

Q. 仮想スレッドは、もう使っていいですか?
A. Java 21以上なら実用段階です。④の注意点(synchronized)を理解したうえで、I/O待ちが多い処理から試すのがおすすめです。Java 24以降なら、その注意点も大きく緩和されています。

Q. 未経験からでも、ここまで到達できますか?
A. できます。RJCの研修はJavaの基礎から始まり、ここは実務で数年書いた先の景色です。段階を踏めば必ず辿り着く場所なんです。

Javaの速さは、コードではなくJVMが決めています

今日の8つに共通していたのは、これでした。

インライン化、エスケープ解析、セーフポイント、コンテナ認識——Javaの性能と安定性は、書いたコードの下で走るJVMの判断で決まっています。

その判断を読める人は、勘ではなく観察で最適化できる。上級の入口は、言語の上ではなく下にあるんですよね。

みなさんがJVMで出会った怪現象、よかったら教えてください。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?