WebView2でwindow.gcがundefinedになる原因と修正方法【Runtime 152対応】

Microsoft Edge WebView2でwindow.gcundefinedになった場合、原因はWebView2 Runtime 152で行われた仕様変更です。2026年8月3日公開のPreview Runtime 152.0.4181.0では、WebViewに対する暗黙のwindow.gc追加が削除されました。そのため、従来は実行できていたwindow.gc()が使えなくなるのは、Runtimeの破損や初期化失敗ではありません。(Microsoft Learn)

対応の基本は、本番コードからwindow.gcへの依存を取り除くことです。メモリリーク調査や自動テストで明示的なGCが必要な場合は、Chrome DevTools ProtocolのHeapProfiler.collectGarbageを使うか、GCを明示的に有効化したテスト専用ランナーへ処理を分離します。

目次

WebView2でwindow.gcがundefinedになる理由

WebView2 Preview Runtime 152.0.4181.0のリリースノートには、バグ修正の一つとして「WebViewに対する暗黙のwindow.gc追加を削除した」と明記されています。つまり、WebView2が自動的に用意していた非標準のGC呼び出し口がなくなったため、次の結果になるのが正常です。(Microsoft Learn)

console.log(typeof window.gc);
// "undefined"

window.gc();
// TypeError: window.gc is not a function

正確には、1.0.4181-prereleaseはWebView2 SDKのバージョンです。実際にwindow.gcの挙動を変更するのは、対応するPreview Runtime 152.0.4181.0です。

項目バージョン今回の変更との関係
WebView2 SDK1.0.4181-prereleaseRuntime 152向けのプレリリースSDK
WebView2 Runtime152.0.4181.0 Preview暗黙のwindow.gc追加を削除
JavaScriptエンジンV8GC機能そのものを実行するエンジン

SDK 1.0.4181-prereleaseは、完全なAPI互換性を得るためにRuntime 152.0.4181.0以降を必要とします。ただし、window.gcの削除はSDKのメソッド削除ではなく、Runtime上のJavaScript実行環境に関する変更です。(Microsoft Learn)

window.gcは標準のJavaScript APIではない

window.gcは、Web標準として常に利用できるAPIではありません。V8にはGC拡張を公開するexpose_gcフラグがありますが、V8のフラグ定義では既定値がfalseになっています。つまり、明示的に公開されていない環境でgcが存在しないこと自体は不自然ではありません。(Chromium)

今回削除されたのは、WebView2が暗黙に追加していた手動GCの入口です。JavaScriptエンジンによる通常の自動ガベージコレクションまで無効になったわけではありません。

また、Microsoftは削除理由の詳細をリリースノートで説明していません。セキュリティ対策、性能改善、Chromiumとの整合性などの理由を推測して断定するのは避けるべきです。少なくとも開発者側では、今後window.gcが常に存在することを前提にしない設計へ変更する必要があります。

影響を受けるコードと発生する症状

特に影響を受けやすいのは、メモリ計測やリークテストの前後でwindow.gc()を直接呼び出しているアプリです。

コードや利用場面Runtime 152での結果主な影響
window.gc()TypeError処理やテストが中断する
globalThis.gc()TypeErrorブラウザー共通コードが失敗する
typeof window.gc === "function"falseGC処理がスキップされる
window.gc?.()何も実行されないテストが実質的に無効になる
メモリ計測直前の強制GC実行されない旧Runtimeと計測条件が変わる
外部ライブラリ内のgc()呼び出しライブラリ次第原因箇所が見つけにくい

次のようにオプショナルチェーンを追加すれば例外は防げます。

window.gc?.();

しかし、これは根本的な修正ではありません。特にメモリテストでは、GCが実行されていないのにテストだけが通過する状態になります。

テストでGCを必須とする場合は、存在しなければ明示的に失敗させる方が安全です。

if (typeof globalThis.gc !== "function") {
  throw new Error(
    "GC機能が有効ではありません。テスト実行環境の設定を確認してください。"
  );
}

globalThis.gc();

最初に確認するべきこと

実際に使用しているRuntimeのバージョンを確認する

NuGetで参照しているSDKのバージョンだけでは、実際に読み込まれたRuntimeを特定できません。Evergreen Runtime、Fixed Version Runtime、Edgeのプレビューチャネルなど、環境によって選択されるバイナリが異なる可能性があるためです。

C#では、WebView2初期化後に次のコードで実際のRuntimeバージョンを確認できます。

await webView.EnsureCoreWebView2Async();

string runtimeVersion =
    webView.CoreWebView2.Environment.BrowserVersionString;

Console.WriteLine($"WebView2 Runtime: {runtimeVersion}");

BrowserVersionStringは、現在のCoreWebView2Environmentが使用しているブラウザーバージョンを返します。Beta、Dev、Canaryの場合は、チャネル名も含まれます。(Microsoft Learn)

障害報告やCIログには、少なくとも次の情報を残してください。

WebView2 SDK: 1.0.4181-prerelease
WebView2 Runtime: 152.0.4181.0 canary
window.gc type: undefined

「SDKを更新したら壊れた」と判断する前に、Runtimeが152系へ切り替わっていないかを確認することが重要です。

JavaScript側で存在確認する

DevToolsのコンソール、またはアプリ内の診断スクリプトで確認します。

console.table({
  type: typeof globalThis.gc,
  exists: "gc" in globalThis
});

Runtime 152で暗黙追加がなくなった環境では、通常は次のような結果になります。

type: "undefined"
exists: false

プロジェクト全体から呼び出し箇所を探す

アプリ本体だけでなく、テストコード、初期化スクリプト、WebView2へ注入するJavaScript、サードパーティー製ライブラリも対象にします。

ripgrepを利用できる場合は、プロジェクトのルートで次のように検索できます。

rg -n --hidden --glob '!node_modules/**' --glob '!dist/**' 'window\.gc|globalThis\.gc|\bgc\s*\(' .

検索時には、次のパターンも見落とさないようにします。

window["gc"]();
globalThis["gc"]();
const forceGc = window.gc;
forceGc();

圧縮済みJavaScriptだけで見つかった場合は、元のソースマップや依存パッケージ側まで確認してください。

window.gcの代わりに何を使うべきか

目的によって適切な代替方法が異なります。

目的推奨する対応本番利用
アプリを正常に動かすwindow.gc依存を削除する
WebView2のメモリテストCDPのHeapProfiler.collectGarbage診断時のみ
純粋なJavaScriptの単体テストNode.jsを--expose-gc付きで実行テストのみ
旧テストの一時的な互換維持テスト専用WebView2へV8フラグを明示不可
通信エラーの観測DiagnosticMonitor用途に応じて検討

本番アプリの正常動作を、手動GCの成否に依存させてはいけません。アプリ側ではイベントリスナー、タイマー、Worker、WebSocket、Object URL、DOM参照など、アプリが所有するリソースを適切に解放します。

WebView2のメモリテストではCDPを使う

WebView2は、プラットフォームAPIとして用意されていないデバッグ・計測機能にChrome DevTools Protocolを利用できます。Microsoftの公式ドキュメントでも、CDPはChromiumベースのブラウザーを検査、デバッグ、プロファイリングするための仕組みとして案内されています。(Microsoft Learn)

CDPには、明示的にGCを要求するHeapProfiler.collectGarbageがあります。WebView2からはCallDevToolsProtocolMethodAsyncで呼び出せます。(Microsoft Learn)

C#でGCを要求する実装例

using Microsoft.Web.WebView2.Core;

private static async Task CollectGarbageForDiagnosticsAsync(
    CoreWebView2 coreWebView2)
{
    if (coreWebView2 is null)
    {
        throw new ArgumentNullException(nameof(coreWebView2));
    }

    await coreWebView2.CallDevToolsProtocolMethodAsync(
        "HeapProfiler.collectGarbage",
        "{}");
}

呼び出し側は、WebView2の初期化後に実行します。

await webView.EnsureCoreWebView2Async();

await CollectGarbageForDiagnosticsAsync(
    webView.CoreWebView2);

CallDevToolsProtocolMethodAsyncの第1引数には{domain}.{method}形式のメソッド名、第2引数にはJSON形式のパラメーターを指定します。HeapProfiler.collectGarbageには必須パラメーターがないため、{}を渡します。(Microsoft Learn)

ただし、HeapProfilerドメインはCDP上で実験的な扱いです。対象Runtimeで利用できることをテストし、失敗時はテストを明示的に失敗させてください。アプリ本体の正常動作をCDPの成功に依存させるべきではありません。

メモリリークテストの流れ

WebView2のメモリリークを調べる場合は、次のように計測条件を固定します。

  1. 対象ページを読み込む
  2. 操作を一定回数繰り返す
  3. イベント、DOM、Workerなどを明示的に破棄する
  4. HeapProfiler.collectGarbageを呼び出す
  5. ヒープスナップショットや保持オブジェクト数を記録する
  6. 同じRuntimeと同じ操作回数で複数回比較する

Windowsのタスクマネージャーに表示されるプロセス使用量だけで合否を決めると、ブラウザープロセス、レンダラープロセス、GPUプロセス、内部キャッシュなどの影響を受けます。瞬間的なメモリ値だけでなく、操作を繰り返したときに保持オブジェクトが増え続けるかを確認する方が実用的です。

純粋なJavaScriptテストは明示設定したランナーへ分離する

DOMやWebView2固有機能を使わない純粋なJavaScriptのメモリテストであれば、Node.jsを--expose-gc付きで起動できます。

node --expose-gc tests/memory-test.mjs

テスト側では、GCが公開されていることを開始時に検証します。

if (typeof globalThis.gc !== "function") {
  throw new Error(
    "node --expose-gc を付けてテストを実行してください。"
  );
}

globalThis.gc();

Node.jsの公式ドキュメントでも、--expose-gcはV8のGC拡張を公開するオプションとして説明されています。(Node.js)

ただし、Node.jsで再現できるのは主にJavaScriptヒープ上の処理です。DOMノード、WebView2のナビゲーション、iframe、Web Worker、ネイティブホストオブジェクトを含むリーク調査は、実際のWebView2とCDPを使用してください。

テスト専用WebView2で明示的にGCを公開する方法

旧テストをすぐにCDPへ移行できない場合は、テスト専用のWebView2環境にV8フラグを明示する方法が考えられます。

using Microsoft.Web.WebView2.Core;

var options = new CoreWebView2EnvironmentOptions
{
    AdditionalBrowserArguments =
        "--js-flags=--expose-gc"
};

var environment =
    await CoreWebView2Environment.CreateAsync(
        browserExecutableFolder: null,
        userDataFolder: testUserDataFolder,
        options: options);

await webView.EnsureCoreWebView2Async(environment);

V8にはexpose_gcフラグがあり、WebView2はCoreWebView2EnvironmentOptions.AdditionalBrowserArgumentsを通じてブラウザー引数を渡せます。(Chromium)

ただし、この方法は次の条件を守った一時的な移行策に限定してください。

  • テスト専用ビルドでのみ有効にする
  • 本番とは別のユーザーデータフォルダーを使う
  • 対象Runtimeごとにtypeof globalThis.gcを確認する
  • gcが存在しない場合はテストを失敗させる
  • CDPへ移行後は設定を削除する

MicrosoftはWebView2のブラウザーフラグについて、開発や診断には利用できるものの、本番アプリでは使わないよう明確に警告しています。フラグは削除、変更される可能性があり、長期サポートも保証されません。(Microsoft Learn)

DiagnosticMonitorはwindow.gcの直接的な代替ではない

SDK 1.0.4181-prereleaseでは、新しい実験的APIとしてDiagnosticMonitorが追加されています。

ただし、このAPIはWebView2から診断イベントを受信する観測専用の仕組みです。リリース時点で公開されている診断カテゴリはNetworkRequestであり、GCを実行したり、JavaScriptヒープを回収したりするAPIではありません。(Microsoft Learn)

したがって、用途は次のように分けます。

診断内容使用する機能
ネットワークリクエストの観測DiagnosticMonitor
JavaScriptヒープのGC要求CDP HeapProfiler.collectGarbage
ヒープスナップショットCDP HeapProfiler.takeHeapSnapshot
純粋なJavaScriptのGCテストNode.js --expose-gc

「新しいDiagnosticMonitorが追加されたため、window.gcが置き換えられた」と理解するのは正確ではありません。

避けるべき対処方法

空のwindow.gcを追加する

次のような互換用コードは、例外を隠すだけです。

window.gc = () => {};

メモリテストは通過してもGCは実行されません。テスト結果の信頼性を失うため、使用しないでください。

例外を握りつぶす

try {
  window.gc();
} catch {
  // 無視
}

これもテスト条件が変わったことを見えなくします。診断処理では、利用できない機能を無視するのではなく、Runtimeバージョンとエラー内容を記録して失敗させる方が安全です。

旧Runtimeへ固定して問題を先送りする

一時的な切り分けとして旧Runtimeと比較することは有効ですが、window.gcを使い続けるためだけに旧Runtimeへ固定するのは恒久策になりません。

将来のRuntimeでも暗黙のwindow.gcが復活する保証はありません。新しいRuntimeへ移行できるよう、テストコード自体を修正してください。

本番アプリへV8フラグを追加する

--js-flags=--expose-gcを本番へ組み込むと、非標準機能への依存が残ります。WebView2のブラウザーフラグは長期的な互換性を保証されないため、テスト専用設定として分離する必要があります。(Microsoft Learn)

Runtime 152への移行チェックリスト

  • 実際に読み込まれたBrowserVersionStringをログへ出力する
  • window.gcglobalThis.gc、間接呼び出しをプロジェクト全体から検索する
  • 呼び出し目的が本番処理か、メモリテストかを分類する
  • 本番処理からはwindow.gc依存を削除する
  • WebView2統合テストはHeapProfiler.collectGarbageへ移行する
  • 純粋なJavaScriptテストは明示設定したテストランナーへ分離する
  • GC機能が使えないときにテストを黙ってスキップしない
  • ブラウザーフラグを本番ビルドへ含めない
  • Stable RuntimeとPreview Runtimeの両方でCIテストを行う
  • メモリ使用量の瞬間値ではなく、保持オブジェクトの増加傾向を比較する

WebView2でwindow.gcundefinedになるのは、Runtime 152で暗黙追加が削除されたことによる正常な挙動です。まずRuntimeバージョンと呼び出し箇所を確認し、本番コードからは依存を取り除いてください。

メモリ診断が目的なら、WebView2ではCDPのHeapProfiler.collectGarbageを使用します。純粋なJavaScriptテストでは、--expose-gcを明示したテストランナーへ分離します。暗黙に存在していた非標準機能へ依存するのではなく、用途ごとに診断手段を明示することが、Runtime更新に強い実装につながります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次