衛星制御ソフトウェアの世界では「ヒープ(プログラム実行中に自由に確保・解放できるメモリ領域)を一切使わない」という設計が採用される場面があります。超低軌道(VLEO、高度250km前後)を周回する商用小型衛星の飛行制御では、メモリ断片化やGC(ガベージコレクション、不要になったメモリを自動回収する仕組み)の一時停止が致命的になるためです。
この話は宇宙開発に限った話ではありません。決済基幹システム、工場の制御システム、医療機器連携システムなど「処理時間の揺らぎ(ジッター)が許されない」業務システムを抱えるエンジニアにとって、同じトレードオフが日常的に発生しています。本記事では、動的メモリ割り当てを制限する設計をいつ採用すべきか、業務システムの文脈で判断基準を整理します。
どんな場面でこの判断が必要になるか
典型的には、処理時間の上限が契約やSLA(サービス品質保証)で明示されているシステムで問題になります。
たとえば高頻度取引システムの注文処理、工場のPLC(プログラマブルロジックコントローラ)と連携するミドルウェア、医療機器のリアルタイム監視ソフトウェアなどです。
衛星制御の事例では100Hz(1秒間に100回)の制御ループを守るために、実行時間のばらつき(ジッター)を約0.03ミリ秒まで抑え込んだと報告されています。業務システムでも「平均応答時間」ではなく「最悪実行時間(WCET、Worst-Case Execution Time)」を基準に設計すべき場面が、実は想像以上に存在します。
判断軸1: WCET保証が契約・法規制レベルで求められるか
最初に確認すべきは、処理時間の上限が「努力目標」なのか「守らなければ損害賠償や事故につながる絶対条件」なのかです。
一般的なWebアプリケーションのAPIは、レスポンスが数百ミリ秒遅れても業務影響は限定的です。一方で衛星の姿勢制御のように、10ミリ秒周期の制御ループが1回でも遅延すれば機体が不安定化するケースでは話が変わります。
業務システムで言えば、証券取引の約定処理、ATMの現金制御、鉄道の信号制御システムなどがこれに近い性質を持ちます。GCの一時停止(STW、Stop The World)が数十ミリ秒発生するだけで致命傷になるなら、ヒープ割り当てを制限する設計の検討対象です。
判断軸2: 稼働期間とメモリ断片化のリスク
2つ目の軸は「システムを再起動せずにどれだけ長く動かし続ける必要があるか」です。
動的メモリ割り当てを長時間繰り返すと、空きメモリが細切れになる「メモリ断片化」が発生します。十分な空きメモリがあるはずなのに、連続した領域が確保できず割り当てに失敗する現象です。
衛星の飛行制御ソフトウェアは、数年単位で再起動せずに稼働し続ける前提で設計されています。これに対し、コンテナオーケストレーション基盤(Kubernetesなど)上で動く業務サービスは、ローリングアップデートやPodの再起動が定期的に発生する構成が一般的です。再起動の頻度が高いシステムでは、断片化のリスクはある程度自然に緩和されます。再起動間隔が長い常駐プロセス(バッチサーバーの長時間ジョブ、組込み機器のファームウェアなど)ほど、静的メモリ確保を検討する価値が上がります。
判断軸3: 開発生産性とのトレードオフ
3つ目は、チームの開発速度にどれだけ影響するかです。
動的メモリ割り当てを完全に排除する設計では、配列サイズやバッファ長をすべてコンパイル時に固定します。これは衛星制御のような「仕様がほぼ固定されたクローズドシステム」では現実的ですが、要件変更の多い業務システムでは開発コストが跳ね上がります。
たとえば顧客管理システムで「登録可能な商品数の上限」を実行時ではなくコンパイル時に固定してしまうと、仕様変更のたびに再コンパイル・再デプロイが必要になります。業務要件の変化速度と、静的確保による柔軟性の喪失を天秤にかける必要があります。
判断軸4: 実行環境のハードウェア制約
最後の軸はターゲットとなるハードウェアです。
衛星搭載のマイコンでは、ARM Cortex-M7やCortex-R5、デュアルコアRISC-Vといった組込み向けプロセッサが使われ、浮動小数点演算ユニット(FPU)の有無によって64bit倍精度と32bit単精度を切り替える設計が取られています。これはメモリやCPUリソースが潤沢ではない組込み環境特有の制約です。クラウド上のサーバーで動く業務システムとは前提が大きく異なります。
一般的なクラウドVMやコンテナ環境では、メモリは比較的潤沢に確保でき、JVMやGo、.NETランタイムのGCも年々改善されています。ハードウェアリソースが制約される組込み機器・エッジデバイス向けのソフトウェアでない限り、ゼロヒープ設計のコストに見合うメリットは出にくいと考えられます。
選択肢の比較
| 方式 | 向いている場面 | 開発コスト | 代表的な技術 |
|---|---|---|---|
| ゼロヒープ(静的メモリ確保のみ) | WCET保証が必須・長期連続稼働 | 高い | C言語の静的配列、Rustのno_std環境 |
| プールアロケータ | ある程度の柔軟性と予測可能性の両立 | 中程度 | オブジェクトプール、スラブアロケータ |
| GC付き言語+チューニング | 開発速度重視・遅延許容度が高い | 低い | Java(G1GC)、Go、.NET |
プールアロケータは、あらかじめ固定サイズのメモリブロックを確保しておき、その範囲内で使い回す方式です。ゼロヒープほど厳格ではないものの、断片化リスクを抑えつつ一定の柔軟性を保てる中間的な選択肢になります。
ケース別の推奨
次のような条件に当てはまるなら、ゼロヒープに近い設計、もしくはプールアロケータの採用を検討する価値があります。
- 処理遅延がSLA違反ではなく安全事故に直結する(工場制御、医療機器、交通制御システム)
- 再起動なしで数ヶ月〜数年の連続稼働が求められる組込み機器
- メモリ容量が数百KB〜数MB程度しかないマイコン上で動く
- 仕様がほぼ固定されており、将来の変更頻度が低い
逆に、次のような業務システムではGC付き言語を使い続け、チューニングで対応する方が現実的です。
- Webサービス・業務アプリのバックエンドAPI(レイテンシ要件がミリ秒〜秒単位)
- Kubernetes上で動き、定期的にPodが再起動される構成
- 仕様変更やスキーマ追加が頻繁に発生する業務ロジック層
あえて見送るべき条件
ゼロヒープ設計の見送りを検討すべき条件もはっきりしています。
チームにC言語やRustの低レベルメモリ管理経験者が少ない場合、導入コストが開発速度を大きく損ないます。また、要件が流動的なSaaSプロダクトのように仕様変更が頻発する領域では、静的確保のメリットよりも硬直化のデメリットが上回ります。
すでにJavaのG1GCやZGC、GoのGCのようにGC停止時間が数ミリ秒以下まで改善されたランタイムを使っていて、現状の遅延要件を満たせているなら、わざわざアーキテクチャを作り変える理由はありません。まずはjstat -gcutilやGoのGODEBUG=gctrace=1といったGC計測コマンドで、実際のGC停止時間を確認するところから始めるのが現実的です。
導入前に確認すること
ゼロヒープ的な設計を検討する前に、次の3点を確認してください。
- 遅延要件は「平均」なのか「最悪値(WCET)」なのか、契約書やSLA文書で明文化されているか
- 現行システムのGC停止時間を
jstatやAPMツール(New Relic、Datadogなど)で実測したか - チームが静的メモリ管理のコードレビュー・デバッグに対応できる体制か
これらを確認したうえで、WCET保証が本当に必須と分かった場合にのみ、静的メモリ確保やプールアロケータの導入を検討するのが安全な進め方です。