Windows Server 2016でのガベージコレクション最適化と安定運用の秘訣

ある日突然、安定稼働していたはずの.NET Web APIが、Windows Server 2016上で頻繁にガベージコレクション(GC)を起こしてユーザーが切断される……。そんなトラブルが発生すると、サービス提供者も利用者も大きなストレスを抱えてしまいます。そこで本記事では、VSS(ボリュームシャドウコピーサービス)や重複排除機能の設定から、.NETのGCやメモリリサイクルの見直しなど、実践的な対処法を詳しく解説します。

Windows Server 2016上での頻繁なGCが引き起こす問題とは

Windows Server 2016上で運用している.NET Web APIがあると、ロードバランサ配下に複数台のサーバーを用意して負荷分散を行うケースは一般的です。しかし、それでも負荷が高まっている環境では、頻繁にガベージコレクションが実行されてしまい、結果的にユーザーが切断されることもあります。
さらに、アプリ開発者の提案によって「メモリ使用量が15GBに達するとアプリのリサイクルを走らせる」という設定を入れている場合、意図的にプロセスが再起動されてしまうため、サービスの稼働が不安定になりかねません。

ガベージコレクションの影響と切断の原因

ガベージコレクション(GC)は、不要になったオブジェクトのメモリを解放する仕組みです。これ自体はシステム運用に必要不可欠なプロセスですが、以下のような問題が起こる可能性があります。

  • GCが長時間実行される
    メモリ使用量が非常に多い場合、一度のGCサイクルでも相当な時間がかかることがあります。その間、アプリケーションの処理速度が落ち、レスポンスが遅延し、最悪の場合ユーザー切断やタイムアウトになるケースも考えられます。
  • フルGCでのパフォーマンス低下
    一部のGC(フルGC)は全体のヒープ領域を走査・解放しようとするため、一時的に大幅なパフォーマンスダウンが発生します。サーバーの台数が増えるほど、この影響は顕著に出ることがあります。
  • IISのアプリプールリサイクルによる切断
    メモリ上限を設定していると、スレッショルドを超えた段階で強制的にアプリケーションプールのリサイクルが行われます。これによりセッションが中断し、ユーザーから見ると「突然接続が切れた」状態になってしまうのです。

表:GCの種類と特徴

種類特徴
Generation 0/1 GC若い世代のオブジェクトを中心に解放する。比較的高速で負荷が小さい。
Generation 2 GC (通常GC)フルスキャンに近いが、Generation 2までに昇格したオブジェクトを対象とする。
フルGC (Full Blocking GC)メモリ全体をスキャンし、不要領域をまとめて解放する。負荷が大きく実行時間も長い。

VSS(ボリュームシャドウコピーサービス)の影響と構成の最適化

本題から少し外れそうに見えるかもしれませんが、Windows Serverでの運用を語る上でVSS(ボリュームシャドウコピーサービス)の構成は見逃せないポイントです。VSSが原因でディスクI/Oが増大していると、アプリケーションのパフォーマンスにも少なからず影響が及びます。

VSSのディフ領域を専用ボリュームへ

VSSのディフ領域(“shadow storage area”)は、デフォルトでは同一ボリューム内に設定されることが多いですが、専用のボリュームへ分離することで以下のメリットが期待できます。

  1. メインボリュームへの負荷を低減
    シャドウコピーの作成や削除が行われる際、ディフ領域が同一ボリューム内にあるとディスクI/Oが増大します。専用ボリュームを割り当てれば、メインボリュームへの負荷を抑えられます。
  2. シャドウコピーの削除抑制
    一般的に、ディフ領域が不足すると古いシャドウコピーが削除されることがありますが、専用ボリュームへ割り当てることで安定的に管理できます。

VSSのディフ領域を移動する例

以下の例は、Cドライブで運用しているVSSをDドライブに移動する場合のPowerShellコマンドです。

# 現在の設定を確認
vssadmin list shadowstorage

# ディフ領域をDドライブに移動
vssadmin resize shadowstorage /for=C: /on=D: /maxsize=20GB

# 設定後の状態を再度確認
vssadmin list shadowstorage

上記の設定を行うことで、VSSのディフ領域をDドライブに確保し、Cドライブへの書き込み負荷を軽減できます。ディスク容量に余裕があれば、より大きな領域を割り当てておくとスナップショットを安定的に保持できるでしょう。

重複排除(dedup)のフルGCを回避・制御する方法

Windows Serverの重複排除機能(Data Deduplication)を有効にしている環境では、定期的にガベージコレクション(GC)が走るように設定されています。これもまた、サーバー全体のパフォーマンスに影響を及ぼす要因となります。

デフォルトの設定と注意点

重複排除が有効なボリュームでは、デフォルトで以下のようなスケジュールが組まれていることが多いです。

  • 毎週1回のGCを実施
  • 約4回に1回(=月に1回ペース)でフルGCを実施

フルGCのタイミングになると、大規模なデータ整理が走り、CPUやメモリ、ディスクI/Oが集中して使用されます。結果として、.NET Web APIや他のサービスに影響が出やすく、ユーザー切断を招くリスクが高まります。

フルGCを手動で実行させたい場合

「業務が少ない時間帯に一括でフルGCを走らせたい」というニーズがある場合、以下のPowerShellコマンドで手動実行できます。

Start-DedupJob -Volume "E:" -Type GarbageCollection -Full

ここで、-Volumeには重複排除を設定しているボリュームのドライブレターを指定します。これを活用することで、計画的にメンテナンスウィンドウの中でフルGCを行い、業務負荷の高い時間帯を避けることが可能です。

フルGCの自動実行を防止するレジストリ設定

定期的に走るフルGCを根本的に停止したい場合には、下記レジストリキーを設定し、DeepGCIntervalを無効化します。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ddpsvc\Settings]
"DeepGCInterval"=dword:ffffffff

クラスター環境ではキーの位置が異なる場合があるため、以下を参照ください。

HKLM\CLUSTER\Dedup\
/v DeepGCInterval /t REG_DWORD /d 0xffffffff

ただし、フルGCを完全に停止してしまうと、データが断片化したまま蓄積されてしまうリスクがあります。ディスク容量の圧迫やパフォーマンス低下を招く可能性もあるため、運用方針に合わせて慎重に検討しましょう。

アプリケーションのメモリ運用を見直す

15GBのメモリ上限に達するとアプリプールをリサイクルする設定を入れているケースでは、負荷が集中する時間帯にしきい値を頻繁に超え、サービス切断が多発しているかもしれません。大容量メモリのWindows Server 2016環境であれば、運用方針を再検討する価値があります。

.NET Framework / .NET CoreのGCモード設定

.NETのGCには大きく分けて「Server GC」「Workstation GC」が存在します。一般的にサーバー環境では、マルチスレッドを活用するServer GCが推奨されます。ただし、Server GCはマシンリソースをより多く使用し、大規模な並列GCを実行する傾向があるため、一度に多くのメモリを扱います。
状況によってはWorkstation GC(単一スレッドや小規模な並列GC)を選択し、最大メモリ消費量を抑制する運用も考えられます。アプリの特性やサーバー台数、利用時間帯のアクセス集中などを総合的に考慮して選びましょう。

Server GCとWorkstation GCの設定例

通常、アプリケーションの.configファイルでGCモードを指定します(.NET Frameworkの場合)。

<configuration>
  <runtime>
    <!-- Server GCを有効にする設定 -->
    <gcServer enabled="true"/>

    <!-- あるいはWorkstation GCを有効にする設定 -->
    <!-- <gcServer enabled="false"/> -->
  </runtime>
</configuration>

.NET Coreの場合は、環境変数COMPLUS_gcServerを用いて切り替える方法もあります。サーバーリソースやアプリの種類に応じて試行錯誤すると良いでしょう。

IISのアプリプールのリサイクル設定を再評価する

IISのアプリケーションプールには、以下のように多彩なリサイクル条件があります。

  • 特定メモリ使用量(Private Bytes / Virtual Memory)
  • 定期的な時間間隔(規定で1740分など)
  • 要求回数

本記事のケースでは、物理メモリ使用量(Private Bytes)が15GBを超えた際にリサイクルしていると考えられます。もしサーバーの物理メモリ容量が十分にあるのであれば、リサイクル基準を引き上げるか、あるいはそもそもメモリ使用量に応じたリサイクルを無効化して、時間ベースのみ(深夜帯に1回など)にする方法も検討可能です。

アプリプールの設定例

アプリプールの設定変更はGUIでも行えますが、PowerShellやAppCmd.exeを使うと複数サーバーに対して一括で適用する際に便利です。

Import-Module WebAdministration

# アプリプール名を指定して、メモリベースのリサイクルを無効化
Set-ItemProperty "IIS:\AppPools\MyAppPool" -Name recycling.periodicRestart.privateMemory -Value 0

# 時間ベースで6時間毎にリサイクルする場合
Set-ItemProperty "IIS:\AppPools\MyAppPool" -Name recycling.periodicRestart.time -Value "06:00:00"

# 変更内容を確認
Get-ItemProperty "IIS:\AppPools\MyAppPool"

このようにメモリや時間など各種しきい値を柔軟に設定できます。負荷状況を見ながら、適切なリサイクルタイミングを決めることがポイントです。

ハードウェアリソースの見直しと分散配置

GCの最適化やリサイクルの設定を見直しても、根本的にサーバーのメモリやCPUリソースが足りなければ対症療法にしかなりません。ロードバランサ配下に6台のサーバーを構成しているということは、ある程度の冗長性は確保されていると思われますが、以下の点を総点検してみましょう。

  1. メモリ容量の増設
    アプリケーションプールのメモリ使用量に余裕を持たせられるよう、物理メモリの容量を増やすことは大きな効果があります。メモリ不足が根本原因ならば、最も手っ取り早く効果的な対処です。
  2. CPUコアの割り当て
    GCはマルチスレッドで並列的に動作することが多いため、CPUコア数が少ないとGC処理自体のオーバーヘッドが高まり、通常のリクエスト処理とリソースを奪い合う形になります。ハイパースレッディングやNUMA構成なども含めて再検討すると良いでしょう。
  3. ストレージ構成の再評価
    VSSのディフ領域や重複排除に利用するボリュームが遅いストレージだと、書き込み・読み取りが増えた際にパフォーマンスがネックになる場合があります。高速なSSDやNVMeドライブを導入することで影響を最小化できます。

複数環境への水平スケールも検討

ロードバランサ配下に複数台あるといっても、すべて同じリソース構成だとピーク時に全台のリソースが足りなくなる可能性もあります。APIゲートウェイなどを利用し、サービスをいくつかの役割に分割してサーバーをスケールアウトさせるアプローチも有効です。
マイクロサービス化やコンテナ化を進めることで、各サービスのメモリ消費を分離しやすくなり、GCの影響を最小限に抑えられるメリットも考えられます。

まとめ:最適なGC管理と運用設計で安定したサービスを

ここまで解説してきたように、Windows Server 2016環境で発生する頻繁なガベージコレクションによるパフォーマンス低下やユーザー切断は、さまざまな要因が複雑に絡み合って生じます。

  1. VSS(ボリュームシャドウコピーサービス)のディフ領域を専用ボリュームへ切り分け
  2. 重複排除(dedup)のフルGCを定期的ではなく必要に応じて手動実行
  3. IISアプリプールのリサイクル設定や.NET GCモードの最適化
  4. サーバーリソース(メモリ・CPU・ストレージ)の再評価とスケールアウト

これらを総合的に検討することで、安定したサービス稼働が期待できます。特に、フルGCの発生を制御することと、アプリケーションのリサイクル条件を見直すことは、即効性が高い対策です。
さらに、ハードウェアリソースの増強や、マイクロサービス化による水平スケールの促進も長期的な視点では重要な判断材料となるでしょう。利用者にとってストレスのない快適なサービスを提供するためにも、総合的なアプローチでWindows Server 2016環境のパフォーマンスを最適化してみてください。

コメント

コメントする