长时间运行应用程序中的CEF/Chromium内存使用

This article was translated from English: Does it need improvement?
Translated
View the article in English

IronPDF使用Chromium进行渲染,因此即使文档已处理并且GC.Collect()运行,PDF生成后进程内存也可能在短时间内保持较高。 有两个来源在起作用:Chromium的非托管本地分配器和IronPDF的浏览器标签池。 在大多数情况下,这是预期的,且不自动意味着有内存泄漏。

这适用于Windows和Linux上的IronPDF和IronPdfEngine,包括Docker、Kubernetes、虚拟机和服务器托管环境,以及使用基于Chromium渲染的任何应用程序,例如.NET、Java。

为什么内存保持高位

在渲染完成后,有两种不同的机制保持内存高位。

Chromium本地分配器保留: Chromium使用大量非托管内存,这些内存驻留在.NET垃圾收集器之外。 当渲染完成后,Chromium通常会保留那些本机内存以供重用,而不是立即将其返回给操作系统。 因此,您的PdfDocument对象可以被正确处理,运行时可以收集托管对象,但整体进程或容器内存仍然可能显示高位。 对此基于Chromium的引擎来说是正常的。

BrowserPool标签重用: IronPDF可以在渲染后保持空闲浏览器标签活着,以便下一个渲染更快。 这些标签在短时间内保存它们的渲染子进程和DOM状态,即使在没有渲染时也会创建一个可见的内存基线。 当启用池时,您通常会看到内存在渲染期间上升,在完成后保持基线,然后随着空闲标签超时和被回收而逐步下降。

为什么GC.Collect()无效

GC.Collect()仅回收托管的.NET内存。 它不强制Chromium将本机内存返回操作系统,也不破坏故意保持活着以重用的温暖BrowserPool标签。 即使正确释放并强制回收后,任务管理器、Docker、Kubernetes或类似New Relic工具所显示的数值可能不会立即下降。

这种情况在长时间运行的应用程序、共享服务环境以及Docker或Kubernetes部署中最为明显。 对于使用IronPdfEngine的Java集成,压力通常出现在引擎进程或容器中,而不是JVM堆中。

BrowserPool默认值

IronPDF在渲染之间保持少量标签温暖以提高性能。 默认值是:

  • BrowserPool.Enabled = true
  • BrowserPool.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
$vbLabelText   $csharpLabel

选项1:完全禁用标签池

当您希望每次渲染后的清理最具确定性时选择此选项。

renderer.RenderingOptions.BrowserPool.Enabled = false;
renderer.RenderingOptions.BrowserPool.Enabled = false;
renderer.RenderingOptions.BrowserPool.Enabled = False
$vbLabelText   $csharpLabel

选项2:继续启用BrowserPool而不保持空闲标签

保留功能开启,但避免在渲染之间保持温热的标签,提供确定的每渲染清理。

renderer.RenderingOptions.BrowserPool.MaxIdleTabs = 0;
renderer.RenderingOptions.BrowserPool.MaxIdleTabs = 0;
$vbLabelText   $csharpLabel

选项3:减少空闲超时时间

一个中间立场:保持一些重用好处,但在流量高峰后让内存更快下降。

renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 10;
renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 10;
renderer.RenderingOptions.BrowserPool.IdleTimeoutSeconds = 10
$vbLabelText   $csharpLabel

在它们之间做出选择:

  • 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
$vbLabelText   $csharpLabel

将您的IronPDF渲调用包装在受限部分中,以便一次仅运行固定数量的作业。

2. 调整或禁用BrowserPool

对于许多对内存敏感的部署来说,这是最有效的第一步。彻底禁用BrowserPool,设定IdleTimeoutSeconds。 这些中的任何一个都可以在不需完全重新启动策略的情况下缩小持久的后渲染基线。

3. 将渲染隔离在单独的工作者中

将PDF渲染移出主要应用程序是生产系统的一种强硬模式。 主要应用程序保持稳定,渲染工作者可以自行重新启动,当该工作者退出时,本机Chromium内存会被完全释放。 在依赖回收之前,确认禁用BrowserPool或设置MaxIdleTabs = 0是否已经为您提供了可预测的每次渲染清理。在Docker或Kubernetes环境、后台作业系统和需要更严格内存隔离的API平台中,隔离方法最有效。

4. 仅在必要时回收工作者

当有硬内存上限且BrowserPool调优仍不够时,在设定数量的作业后回收渲染进程或工作者容器。 这可能意味着在固定的批量后重新启动专用工作者,在Kubernetes中旋转引擎容器,或将渲染隔离到可以独立重新启动的服务中。 将此视为晚些时候的步骤,而非您首次的举措。

它可能是泄漏的情况

渲染后内存的上升并不自动意味着泄漏。 基于Chromium的渲染有两种需要理解的重用:分配器级重用,其中Chromium在内部保留本机内存页(正常情况,无法直接控制),和标签级重用,其中BrowserPool保持完整的标签在渲染之间活着(可配置,通常是您在监视工具中看到更大贡献者)。

当在渲染期间内存上升,稳定而不是无限制增长,保持短暂高位,而后随着空闲标签超时或为下一次渲染保持可用时,这样的模式通常是可预期的。

当进一步调查以下情况时:

  • 即使在相似工作负载下,内存仍持续增加而不稳定
  • 即使在减少并发性后,内存仍持续增长
  • 即使在禁用BrowserPool或设定MaxIdleTabs = 0后,内存仍持续增长
  • 最小化复现显示相同的时间模式,并具有受控输入

什么时候联系支持

如果您可以重现以下所有情况,请联系技术支持

  1. 在受控工作负载下,内存持续增长而不稳定
  2. 限制并发后问题仍然存在
  3. 在禁用BrowserPool或设定MaxIdleTabs = 0后,问题仍然存在
  4. 您可以使用一个最小的示例项目重现问题

开票时,请包括您的IronPDF版本、操作系统和托管环境、Docker或Kubernetes详细信息(如果适用)、编程语言、渲染频率和并发级别、内存图表或监控屏幕截图,以及一个最小可重现的示例。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

准备开始了吗?
Nuget 下载 20,296,129 | 版本: 2026.7 刚刚发布
Still Scrolling Icon

还在滚动吗?

想快速获得证据? PM > Install-Package IronPdf
运行示例看着你的HTML代码变成PDF文件。