長時間実行アプリケーションでのCEF/Chromiumのメモリ使用
IronPDFはChromiumでレンダリングするため、PDF生成後でもドキュメントが破棄され、GC.Collect()が実行されたときに、プロセスメモリが短期間高く保たれることがあります。 2つの要素が影響します:Chromiumの管理外のネイティブアロケータとIronPDFのブラウザタブプーリングです。 これは多くの場合予期される事態であり、自動的にメモリリークを意味するものではありません。
これは、WindowsとLinux上のIronPDFとIronPdfEngine、Docker、Kubernetes、VM、およびサーバーホスト環境、そしてChromiumベースのレンダリングを使用する.NET、Java、その他のアプリケーションに適用されます。
メモリが高く保たれる理由
2つの異なるメカニズムがレンダリングが完了した後もメモリを高く保ちます。
Chromiumネイティブアロケータ保持:Chromiumは、大量の管理外メモリを使用し、.NETガベージコレクターの外に存在します。 レンダリングが完了すると、ChromiumはそのネイティブメモリをすぐにOSに返さずに再利用のために引き続き予約することが多いです。 したがって、あなたのPdfDocumentオブジェクトが正しく破棄され、ランタイムが管理されたオブジェクトを収集し、全体のプロセスやコンテナメモリは依然として高いと見なされる可能性があります。 これはChromiumベースのエンジンにとって通常のことです。
BrowserPoolタブの再利用:IronPDFは、次のレンダリングが迅速に開始できるようにレンダリング後においてアイドル状態のブラウザタブを生かし続けることがあります。 それらのタブは、短期間の間レンダラーサブプロセスとDOM状態を保持し、何もレンダリングされていないときでも目に見えるメモリベースラインを作成します。 プーリングがオンになっている場合、通常はレンダリング中にメモリが上昇し、それが完了した後にベースラインで保持され、アイドルタブがタイムアウトして回収されるにつれて段階的に減少します。
なぜGC.Collect()は役に立たないのか
GC.Collect()は管理された.NETメモリのみを回収します。 ChromiumがネイティブラメモリをOSに戻すように強制することはなく、再利用のために意図的に生かされる温かいBrowserPoolタブを破棄することもありません。 正しい廃棄と強制収集を行っても、タスクマネージャー、Docker、Kubernetes、またはNew Relicのようなツールによって表示される数値はすぐには減少しないかもしれません。
この影響は、長時間実行アプリケーション、共有サービス環境、およびDockerやKubernetesデプロイメントで最も顕著です。 Java統合でIronPdfEngineを使用する場合、通常エンジンプロセスやコンテナに圧力が現れ、JVMヒープには現れません。
BrowserPoolデフォルト
IronPDFは、パフォーマンスを向上させるためにレンダリング間で少数のタブを温かく保っています。 デフォルトは以下の通りです:
BrowserPool.Enabled = trueBrowserPool.MaxIdleTabs = min(max(ProcessorCount / 2, 1), 4)BrowserPool.IdleTimeoutSeconds = 30
このため、アクティブなレンダリングがない場合でも、プロセスは1〜4つのアイドルタブを一時的に生かし続け、それぞれがネイティブメモリとサブプロセスリソースを保持します。アイドルタイムアウトが終了するまで継続します。 レンダリングされた内容と環境に応じて、アイドルタブは一部のワークロードで約50〜100 MBの見た目にわかる量を保持することができます。 その上昇したベースラインは、最後のレンダリング後約30秒間持続することがあり、段階的に減少します。
解決策
環境がメモリ制約されている場合、BrowserPool設定が最初に調整するものです。 デフォルトは以下のようです:
renderer.RenderingOptions.BrowserPool.Enabled = true; // default
renderer.RenderingOptions.BrowserPool.MaxIdleTabs = 2; // default is CPU/2, clamped to 1-4
renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 30; // default
renderer.RenderingOptions.BrowserPool.Enabled = true; // default
renderer.RenderingOptions.BrowserPool.MaxIdleTabs = 2; // default is CPU/2, clamped to 1-4
renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 30; // default
オプション 1: タブプーリングを完全に無効にする
レンダリング後の最も決定的なクリーンアップが必要なときにこれを検討します。
renderer.RenderingOptions.BrowserPool.Enabled = false;
renderer.RenderingOptions.BrowserPool.Enabled = false;
renderer.RenderingOptions.BrowserPool.Enabled = False
オプション 2: アイデルタブを保持せずにBrowserPoolを有効にしておく
機能をオンにしておきますが、レンダリング間に温かいタブを保持することを避け、レンダリングごとの決定的なクリーンアップを提供します。
renderer.RenderingOptions.BrowserPool.MaxIdleTabs = 0;
renderer.RenderingOptions.BrowserPool.MaxIdleTabs = 0;
オプション 3: アイドルタイムアウトの短縮
中程度の解決策:再利用の利点を一部保持しながら、バーストトラフィック後にメモリを早く減少させます。
renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 10;
renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 10;
renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 10
これらの選択肢の選択:
Enabled = false:メモリの安定性はウォームスタートのパフォーマンスよりも重要です。MaxIdleTabs = 0:機能を完全に無効にすることなく、レンダーごとのクリーンアップを決定的にしたい。- 低い
IdleTimeoutSeconds:ワークロードが突発的に到着し、その後すぐにメモリを解放したい。
長時間実行アプリケーションのための戦略
もしあなたのアプリケーションがメモリを一貫して低く保つ必要がある場合には、プロセスリサイクリングに到達する前に、BrowserPoolの調整、最初に、それに取り組んでください。
1. 同時レンダリングを制限する
複数のChromiumレンダージョブを同時に実行すると、ネイティブメモリ圧力が急激に上昇します。 並行してレンダリングする場合は、セマフォを使って同時実行するジョブの実行数を制限します:
private static readonly SemaphoreSlim RenderSemaphore = new(2);
public async Task<t> RunRenderAsync<t>(Func<t> renderWork)
{
await RenderSemaphore.WaitAsync();
try
{
return renderWork();
}
finally
{
RenderSemaphore.Release();
}
}
private static readonly SemaphoreSlim RenderSemaphore = new(2);
public async Task<t> RunRenderAsync<t>(Func<t> renderWork)
{
await RenderSemaphore.WaitAsync();
try
{
return renderWork();
}
finally
{
RenderSemaphore.Release();
}
}
Imports System.Threading
Private Shared ReadOnly RenderSemaphore As New SemaphoreSlim(2)
Public Async Function RunRenderAsync(Of T)(renderWork As Func(Of T)) As Task(Of T)
Await RenderSemaphore.WaitAsync()
Try
Return renderWork()
Finally
RenderSemaphore.Release()
End Try
End Function
セマフォを使ってIronPDFレンダリング呼び出しを制限し、一度に実行されるジョブの数を固定します。
2. BrowserPoolをチューニングするか無効にする
多くのメモリに敏感なデプロイメントでは、これが最も効果的な最初のステップです。BrowserPoolを完全に無効にし、IdleTimeoutSecondsを低く設定してください。 どれかこれらのどれかにより、プロセス全体を再起動せずに、持続するレンダー後のベースラインを縮小できます。
3. 別のワーカーでレンダリングを分離する
PDFレンダリングをメインアプリケーションから移すことは、プロダクションシステムにとって強力なパターンです。 メインアプリケーションは安定しており、レンダリングワーカーは単独で再起動でき、ネイティブのChromiumメモリはそのワーカーが終了するときに完全に解放されます。 リサイクルに依存する前に、BrowserPoolを無効にしたり、MaxIdleTabs = 0を設定することで、レンダーごとのクリーンアップが予測可能かどうかを確認してください。アイソレーションアプローチは、DockerやKubernetes環境、バックグラウンドジョブシステム、APIプラットフォームで、より厳密なメモリアイソレーションが必要な場合に最も効果があります。
4. 必要なときにのみワーカーをリサイクルする
メモリの上限が厳しい場合やBrowserPoolの調整で十分でない場合、一連のジョブの後にレンダリングプロセスまたはワーカーコンテナを再利用してください。 それは、一定のバッチサイズ後に専用のワーカーを再起動することや、Kubernetesでエンジンのコンテナを回転させること、また独立して再起動できるサービスにレンダリングを分離することを意味するかもしれません。 これは最初の手段ではなく、後の手段として考えてください。
メモリリークの可能性があるとき
レンダリング後のメモリの上昇は、自動的にリークの意味ではありません。 Chromiumベースのレンダリングには、理解するべき2つの再利用があります。アロケーターレベルの再利用:Chromiumが内部でネイティブメモリページを予約する(通常で直接制御できない)、および<stron>タブレベルの再利用:BrowserPoolがレンダリング間に完全なタブを保持する(設定可能で、監視ツールで見るものにしばしば大きく寄与する)。
レンダリング中にメモリが上昇し、無制限に増加する代わりに安定し、後で放置されるか次のレンダリングに使用可能な状態でわずかに上昇するというパターンは通常期待される。
以下の場合にさらに調査してください:
- 同様のワークロードの下でメモリが安定せずに増加し続ける
- 同時実行数を減らした後もメモリが増加し続ける
- BrowserPoolを無効にしたり、
MaxIdleTabs = 0を設定した後でもメモリが増え続ける - 最小限の再現が制御された入力で同じパターンを示す
サポートに連絡する場合
技術サポートにお問い合わせください。以下を再現できる場合:
- 制御されたワークロードの下で安定せずにメモリが増加し続ける
- 同時実行数を制限した後も問題が続く
- BrowserPoolを無効にしたり、
MaxIdleTabs = 0を設定した後でも問題が続く - 最小限のサンプルプロジェクトで再現できる
チケットを開く際には、IronPDFのバージョン、OSとホスティング環境、DockerまたはKubernetesの詳細(該当する場合)、プログラミング言語、レンダリング頻度と同時実行レベル、メモリグラフまたは監視スクリーンショット、最小再現可能なサンプルを含めてください。

