cmsとgcの基本と仕組みを徹底わかりやすく解説

目次

はじめに

概要

本記事はJavaのCMS GC(Concurrent Mark-Sweep Garbage Collector)について、基礎から運用までを丁寧に解説します。CMS GCの仕組み、特徴、他のGC方式との違い、チューニング方法や注意点、実践事例までを網羅します。

本記事の目的

Javaアプリケーションの停止時間や応答性を改善したい技術者に、CMS GCの理解と実運用に役立つ知識を提供します。専門用語は最小限にし、具体例で補足します。

対象読者

  • Javaの基本知識を持つエンジニア
  • 運用担当者や性能改善に関心がある方
  • GCの選定やチューニングを学びたい方

読み方の案内

第2章以降で仕組みや具体的な設定、比較、チューニング例を順に説明します。まずは本章で全体像をつかんでください。

期待できる効果

CMS GCを理解すると、アプリの停止時間を短く保ちながら安定運用する選択肢が増えます。実務で使える知識を身につけてください。

CMS GCとは何か?

概要

CMS GC(Concurrent Mark-Sweep Garbage Collector)は、Javaなどで使われるガベージコレクタの一つです。目的はアプリケーションの停止時間(Stop the World)を小さくすることです。メモリの「マーク」と「スイープ」をアプリケーションと並行して進め、応答性を重視するシステムに向いています。

主な特徴

  • 並行処理が中心です。多くの作業をアプリケーション実行中に行い、長い停止を避けます。
  • 必要な場面で短い停止を行います。完全に停止しないわけではありません。
  • メモリの断片化が起きやすく、場合によっては追加の処理やフルGCが必要になります。

どんな場面に向くか

応答時間が重要なサーバーや対話型のサービスに向きます。たとえばWebサーバーやチャットサービスのように、何秒も応答が止まると困る環境で有効です。

簡単なイメージ

  1. マークフェーズ:生きているオブジェクトに印を付けます(この一部を並行で行います)。
  2. スイープフェーズ:印のない領域を解放します。
  3. リマーケや短い停止を伴う最終処理で整えます。

この章ではCMS GCの全体像と使いどころを分かりやすく紹介しました。次章で仕組みと詳細に進みます。

ガベージコレクションの基本とCMS GCの役割

ガベージコレクション(GC)とは

ガベージコレクションは、プログラムが使わなくなったオブジェクトを自動で見つけてメモリを解放する仕組みです。手動でメモリ解放する必要がなくなり、メモリ不足や破壊的な不具合を減らせます。

基本的な仕組み(やさしい説明)

  • ルート(スタックや静的参照)から到達できるオブジェクトを「生きている」と判断します。
  • 到達できないオブジェクトを「不要」とみなし、回収します。
  • 実装には「マーク&スイープ」「コピー」「参照カウント」などの方式があります。世代別に分けると効率が上がります。

世代別GCとヒープ構造

ヒープは若い世代(Young)と古い世代(Old)に分かれます。多くのオブジェクトは短命なので若い世代で素早く回収し、長生きするものだけが古い世代へ移ります。これにより頻繁な回収を効率化します。

停止時間(Pause)の影響

従来のGCは処理中にアプリケーションを一時停止させることが多く、応答性に悪影響を与えます。特に大きなヒープでは長い停止時間が発生しやすく、ユーザー体験やサービスの安定性を損ないます。

CMS GCの役割

Concurrent Mark-Sweep(CMS)は、古い世代の停止時間を短くするために設計されています。主な特徴は次の通りです。
– ほとんどの処理をアプリケーションと並行(コンカレント)で行う
– 初期の短い停止(Initial Mark)と最後の短い停止(Remark)だけを要求する
– 停止時間を短縮し、レイテンシを改善する

このため、大規模で応答性が重要なJavaアプリケーションに適しています。ただし断片化やCPU負荷など別の問題も生じる点に注意してください。

CMS GCの仕組みと特徴

概要

CMS(Concurrent Mark-Sweep)は停止時間を短くすることを目指した、主に古い世代を対象とするガベージコレクタです。処理をいくつかのフェーズに分け、アプリケーションの稼働をできるだけ止さないように設計されています。

各フェーズの説明

  • 初期マーク(Initial Mark):アプリを短時間停止して、スレッドスタックや静的参照などのルートから到達可能なオブジェクトをマークします。停止は通常ごく短時間です。
  • 並行マーク(Concurrent Mark):アプリ動作と並行して、初期マークで見つけた根から到達するオブジェクトをさらに探索していきます。ここではアプリは継続して動きます。
  • 再マーク(Remark):並行マーク中にアプリが更新した参照の差分を補正するため、短時間停止して追加のマークを行います。初期マークよりは少し長くなることがありますが、短時間で終わります。
  • 並行スイープ(Concurrent Sweep):マークされなかった不要オブジェクトを解放します。これもアプリと並行して進みます。

特徴と注意点

  • 停止時間が短い:全体として長時間のフル停止を避けられるため、応答性が重要なサービスに向きます。
  • メモリ断片化:オブジェクトを逐次解放するため断片化が起きやすく、断片化が進むと最終的にFull GCが必要になる場合があります。
  • CPU負荷:並行処理のためCPUリソースを追加で使います。CPUが不足すると逆に影響が出ます。
  • 変更追跡(ライトバリア):並行中のオブジェクト更新を追跡する仕組みを使います。これにより正確なマークが可能になります。
  • 若い世代との関係:多くの実装で若い世代は別方式でGCされます。若い世代の長い停止はCMSの利点を減らします。

実際のイメージ(簡単な例)

Webサーバがリクエスト処理を続ける間にCMSが並行で不要オブジェクトを探して解放します。時々短い停止が入りますが、通常はユーザー体感の遅延を小さく保てます。一方で断片化やヒープが逼迫すると、長いFull GCが発生することがあります。

CMS GCと他GC方式の比較

概要比較

GC方式 特徴 用途例
Serial GC 単一スレッドで停止時間が長め 小規模アプリ、開発環境
Parallel GC 複数スレッドで高スループット バッチ処理、計算集約型サーバ
CMS GC 停止時間短縮・並行処理重視 Webサーバ、応答性重視のサービス
G1 GC 停止時間予測・断片化抑制 大規模な複雑アプリケーション
ZGC 極低遅延、大ヒープ対応 超低遅延が必要なサービス

各方式の特徴(具体例で説明)

  • Serial: すべてのスレッドを止めて掃除します。小さなコマンドラインツールやテストで十分です。
  • Parallel: GCを並列化して処理時間を短くします。大量データを一括処理するバッチで有効です。
  • CMS: 多くの作業をアプリ実行中に並行して行い、短い停止で応答を保ちます。WebアプリやAPIサーバに向きます。
  • G1: ヒープを領域に分けて断片化を抑え、停止時間の目標を守りやすいです。大規模サービスで採用されます。
  • ZGC: 非常に低い停止を目指し、大きなヒープでもスケールします。高頻度取引などで使われます。

CMSを選ぶ際の注意点

  • 利点: 応答性を優先するシステムで停止が短くなる点が魅力です。
  • 注意: 断片化が起きやすく、CPU負荷が増えることがある点に注意してください。古いJVMでは有効ですが、最近はG1やZGCのほうが運用負担が小さい場合があります。

選び方の目安

  • 応答性が最優先で、ヒープがそれほど大きくないならCMSを検討します。例: レスポンス重視のWeb API。
  • 大きなヒープや停止時間の予測が重要ならG1やZGCを優先します。例: 大規模マイクロサービスや金融系システム。

運用ではGCログを確認し、実際の停止時間とCPU影響を見て切り替えやチューニングを行ってください。

CMS GCのチューニング・最適化ポイント

1) 準備:ログ取得と解析

GCログを必ず有効化します。例:-Xlog:gc*:file=gc.log:tags,uptime,time,level。ログをGCViewerやGCEasyで見れば、頻度・停止時間・ヒープ推移が分かります。

2) ヒープサイズ調整

-Xms(初期)と-Xmx(最大)を同じにして安定させることを推奨します。小さすぎると頻繁にGCが起き、大きすぎるとフルGCやメモリ破片化を招きます。負荷試験で適切な領域を決めます。

3) CMS固有パラメータ

-XX:+UseConcMarkSweepGC を指定している前提で、よく使う設定例:
– CMS開始閾値:-XX:CMSInitiatingOccupancyFraction=70(ヒープ70%で並行マーキング開始)
– 固定開始:-XX:+UseCMSInitiatingOccupancyOnly(テスト時に有効)
– クラスアンロード:-XX:+CMSClassUnloadingEnabled(PermGen/Metaspace回収)

開始閾値はアプリ特性で上下します。遅い開始は長いプレスを招き、早すぎる開始は無駄なCPU消費になります。

4) 若い世代と昇格制御

  • ParNew(若年世代の並列GC)を併用し、-XX:SurvivorRatio や -XX:MaxTenuringThreshold を調整して過剰な昇格を避けます。昇格が多いとCMSの負担が増えます。

5) スレッド数とCPU配分

  • 並行GCスレッド:-XX:ConcGCThreads=<数>(CPUコア数に応じて設定)
  • 若年世代の並列スレッド:-XX:ParallelGCThreads=<数>(ParNewに影響)
    CPUを使いすぎるとアプリレスポンスが悪化するので、負荷試験で最適値を見つけます。

6) 運用上の注意と試験

本番で設定変更する前に負荷試験で効果を確かめます。ログを継続的に監視し、フルGCや長い停止が出たら閾値やヒープを見直します。定期的にチューニングを繰り返して下さい。

CMS GCのデメリット・注意点

CMS(Concurrent Mark-Sweep)は停止時間を短くできますが、運用ではいくつか注意点があります。以下に分かりやすくまとめます。

  • メモリの断片化
    多くの小さなオブジェクトを繰り返し割り当てると、空き領域が細かく分かれ、大きな配列などを確保できなくなることがあります。例:長時間動くサーバで一部の処理が大きなバッファを要求すると、割り当てに失敗してフルGCを招くことがあります。

  • 若い世代との連携で発生するフルGC
    若い世代(短命オブジェクト)のGC中に、CMSの並行処理が追いつかないと「Concurrent Mode Failure」が起き、フルGCに落ちます。結果として長い停止時間が発生します。

  • CPU負荷とトレードオフ
    CMSは並行処理でCPUを使います。CPUが不足すると逆に性能が悪化します。低負荷環境なら恩恵が薄くなります。

  • 設定や監視の重要性
    しきい値や開始タイミングを適切に調整し、GCログやアプリの遅延を監視してください。具体例:GCログで”concurrent mode”関連のメッセージが増える場合は開始閾値を下げる検討をします。

  • 将来性(非推奨と移行検討)
    Javaの進化でCMSは非推奨化されつつあり、G1GCやZGCなどへの移行が推奨される場面が増えます。移行時は性能実測と段階的な切替を行ってください。

まとめると、CMSは短停⽌を実現しますが、断片化や若い世代との不整合、CPU負荷などのリスクがあります。運用ではログ監視と段階的なテスト、必要に応じたGCの見直しを行ってください。

CMS GCの活用事例と今後の動向

概要

CMS GCは応答遅延を抑える目的で多くの現場で採用されました。特にWebサーバやリアルタイム性が求められるシステムで効果を発揮します。

活用事例

  • Webアプリケーション(例:オンライン決済やECサイト)
  • ユーザーの応答速度を優先し、フル停止を短くするためにCMSを使います。
  • 金融系や低遅延処理(例:マーケットデータ配信)
  • 数十ミリ秒の遅延が許されない場面で、停止時間の短縮に寄与します。
  • レガシーなミドルウェア
  • 古いJVM環境での互換性を保ちながら安定運用できます。

移行と実運用のポイント

  • まず計測します。GCログやJFRで停止時間やメモリ状況を把握してください。
  • ステージングでG1やZGCと比較試験を行い、応答性とスループットを確認します。
  • 段階的に切り替えます。全トラフィックを一度に移さず、カナリアリリースで検証します。
  • 断片化対策やヒープ調整を行い、モニタリングを強化してください。

今後の動向

G1GCがデフォルト化し、ZGCやShenandoahが断片化耐性や停止時間予測性で注目を集めています。多くの新規プロジェクトはこれらへ移行する傾向です。ただし、アップグレードが難しい既存システムや特定のチューニング要件がある場合は、CMSを当面維持する選択も合理的です。

実務上は、まず測定してから判断し、移行は段階的に進めることをおすすめします。

まとめと技術者へのアドバイス

目的の再確認

CMS GCは、停止時間を短く保ちながらメモリを回収する方式です。本書で学んだ仕組みやログ解析は、実運用での安定性向上に直結します。

実践的アドバイス

  1. 計測を最優先にする:まず本番に近い負荷でGCログを取得し、頻度と停止時間を確認します。具体例:ピーク時間の60分間でのGC発生回数を測る。
  2. 小さく刻む調整:若い世代とCMSの閾値を少しずつ変えて様子を見ます。急に大きく変えず、段階的に検証してください。
  3. ツールを活用する:GCログ解析ツールやプロファイラでボトルネックを可視化します。数字で判断すると間違いが少なくなります。

注意点(よくある落とし穴)

  • 断片化が進むと回収が追いつかず長い停止につながることがあります。メモリ使用量に余裕を持たせると良いです。
  • Javaのバージョンやアプリ特性で最適解が変わります。G1やZGCが適する場合もあるため、選択肢を比較してください。

技術者への心構え

まず観察し、小さな変更を繰り返して検証する習慣をつけてください。運用で得たデータが最良の指針になります。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

目次